├── .babelrc
├── .coveralls.yml
├── .editorconfig
├── .eslintignore
├── .eslintrc.js
├── .gitattributes
├── .gitignore
├── .nvmrc
├── .nycrc
├── .sweet-changelogs.js
├── .travis.yml
├── CONTRIBUTING.md
├── HISTORY.md
├── ISSUE_TEMPLATE.md
├── LICENSE
├── README.md
├── bower.json
├── dist
├── react-select-plus.css
├── react-select-plus.es.js
├── react-select-plus.js
├── react-select-plus.min.css
└── react-select-plus.min.js
├── examples
├── dist
│ ├── .gitignore
│ ├── app.js
│ ├── bundle.js
│ ├── common.js
│ ├── example.css
│ ├── index.html
│ ├── standalone.html
│ └── standalone.js
└── src
│ ├── .gitignore
│ ├── .npmignore
│ ├── app.js
│ ├── components
│ ├── BooleanSelect.js
│ ├── Cities.js
│ ├── Contributors.js
│ ├── Creatable.js
│ ├── CustomComponents.js
│ ├── CustomRender.js
│ ├── GithubUsers.js
│ ├── GroupedOptionsField.js
│ ├── Multiselect.js
│ ├── NumericSelect.js
│ ├── States.js
│ └── Virtualized.js
│ ├── data
│ ├── cities.js
│ ├── contributors.js
│ ├── states.js
│ └── users.js
│ ├── example.less
│ ├── favicon.ico
│ ├── index.html
│ └── standalone.html
├── less
├── control.less
├── default.less
├── menu.less
├── mixins.less
├── multi.less
├── select.less
└── spinner.less
├── lib
├── Async.js
├── AsyncCreatable.js
├── Creatable.js
├── Dropdown.js
├── Option.js
├── OptionGroup.js
├── Select.js
├── Value.js
├── index.js
└── utils
│ ├── blockEvent.js
│ ├── defaultArrowRenderer.js
│ ├── defaultClearRenderer.js
│ ├── defaultFilterOptions.js
│ ├── defaultMenuRenderer.js
│ ├── stripDiacritics.js
│ └── trim.js
├── package-lock.json
├── package-scripts.js
├── package.json
├── rollup.config.js
├── scss
├── components.scss
├── control.scss
├── default.scss
├── menu.scss
├── mixins.scss
├── multi.scss
├── select.scss
└── spinner.scss
├── src
├── Async.js
├── AsyncCreatable.js
├── Creatable.js
├── Dropdown.js
├── Option.js
├── OptionGroup.js
├── Select.js
├── Value.js
├── index.js
├── index.umd.js
└── utils
│ ├── blockEvent.js
│ ├── defaultArrowRenderer.js
│ ├── defaultClearRenderer.js
│ ├── defaultFilterOptions.js
│ ├── defaultMenuRenderer.js
│ ├── stripDiacritics.js
│ └── trim.js
├── test
├── Async-test.js
├── AsyncCreatable-test.js
├── Creatable-test.js
├── Option-test.js
├── Select-test.js
└── Value-test.js
├── testHelpers
├── jsdomHelper.js
└── nodeListType.js
├── wallaby.js
└── webpack.config.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "ignore": ["node-modules/**", "src/index.umd.js"],
3 | "presets": [ "env", "stage-0", "react"],
4 | "env": {
5 | "test": {
6 | "plugins": ["istanbul"],
7 | },
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/.coveralls.yml:
--------------------------------------------------------------------------------
1 | service-name: travis-ci
2 | repo_token: itdMRdBNgDK8Gb5nIA63zVMEryaxTQxkR
3 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # This file is for unifying the coding style for different editors and IDEs
2 | # editorconfig.org
3 | root = true
4 |
5 | [*]
6 | end_of_line = lf
7 | charset = utf-8
8 | trim_trailing_whitespace = true
9 | insert_final_newline = true
10 | indent_style = tab
11 | translate_tabs_to_spaces = false
12 |
13 | [*.json]
14 | indent_style = space
15 | indent_size = 2
16 |
17 | [*.yml]
18 | indent_style = space
19 | indent_size = 2
20 |
21 | [*.md]
22 | trim_trailing_whitespace = false
23 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | lib/*
2 | dist/*
3 | coverage/*
4 | examples/dist/*
5 | node_modules/*
6 | bower_components/*
7 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | parserOptions: {
3 | ecmaVersion: 6,
4 | sourceType: 'module',
5 | ecmaFeatures: {
6 | experimentalObjectRestSpread: true,
7 | jsx: true,
8 | },
9 | },
10 | env: {
11 | browser: true,
12 | es6: true,
13 | node: true,
14 | },
15 | plugins: ['react'],
16 | rules: {
17 | 'curly': [2, 'multi-line'],
18 | 'jsx-quotes': 1,
19 | 'no-shadow': 0,
20 | 'no-trailing-spaces': 0,
21 | 'no-underscore-dangle': 0,
22 | 'no-unused-expressions': 0,
23 | 'object-curly-spacing': [1, 'always'],
24 | 'quotes': [2, 'single', 'avoid-escape'],
25 | 'react/jsx-boolean-value': 1,
26 | 'react/jsx-no-undef': 1,
27 | 'react/jsx-uses-react': 1,
28 | 'react/jsx-uses-vars': 1,
29 | 'react/jsx-wrap-multilines': 1,
30 | 'react/no-did-mount-set-state': 1,
31 | 'react/no-did-update-set-state': 1,
32 | 'react/no-unknown-property': 1,
33 | 'react/prop-types': 1,
34 | 'react/react-in-jsx-scope': 1,
35 | 'react/self-closing-comp': 1,
36 | 'react/sort-comp': 1,
37 | 'react/sort-prop-types': 1,
38 | 'semi': 2,
39 | 'strict': 0,
40 | },
41 | };
42 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | dist/* binary
2 | examples/dist/* binary
3 | lib/* binary
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 |
5 | # Runtime data
6 | pids
7 | *.pid
8 | *.seed
9 |
10 | # Coverage tools
11 | lib-cov
12 | coverage
13 | .nyc_output
14 |
15 | # Dependency directory
16 | node_modules
17 | bower_components
18 |
19 | # Publish directory
20 | .publish
21 |
22 | # Editor artifacts
23 | .idea
24 |
25 | # Other
26 | .DS_Store
27 | .env
28 | yarn.lock
29 |
--------------------------------------------------------------------------------
/.nvmrc:
--------------------------------------------------------------------------------
1 | 8.4.0
2 |
--------------------------------------------------------------------------------
/.nycrc:
--------------------------------------------------------------------------------
1 | {
2 | "reporter": [
3 | "lcov",
4 | "text-summary"
5 | ],
6 | "require": [
7 | "babel-register"
8 | ],
9 | "sourceMap": false,
10 | "instrument": false,
11 | "report-dir": "./coverage"
12 | }
13 |
--------------------------------------------------------------------------------
/.sweet-changelogs.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | filename: 'HISTORY.md',
3 | message: ({ pr, user }) =>
4 | `* ${pr.title}, thanks [${user.name ||
5 | user.login}](${user.url}) - [see PR](${pr.url})`,
6 | };
7 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: false
2 | language: node_js
3 | node_js:
4 | - "v4"
5 | script:
6 | - npm run lint
7 | - npm run test
8 | - npm run coveralls
9 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | Thanks for your interest in React-Select. All forms of contribution are
4 | welcome, from issue reports to PRs and documentation / write-ups.
5 |
6 | * We use node.js v4+ for development and testing. Due to incompatibilities with
7 | JSDOM and older versions of node.js, you'll need to use node 4 and above to run the
8 | tests. If you can't install node v4 or above as your "default" node installation, you
9 | could try using [nvm](https://github.com/creationix/nvm) to install multiple
10 | versions concurrently.
11 | * If you're upgrading your node.js 0.x environment, it's sometimes necessary
12 | to remove the node_modules directory under react-select, and run npm install
13 | again, in order to ensure all the correct dependencies for the new version
14 | of node.js (as a minimum, you'll need to remove the `jsdom` module, and
15 | reinstall that).
16 |
17 | Before you open a PR:
18 |
19 | * If you're planning to add or change a major feature in a PR, please ensure
20 | the change is aligned with the project roadmap by opening an issue first,
21 | especially if you're going to spend a lot of time on it.
22 | * In development, run `npm start` to build (+watch) the project source, and run
23 | the [development server](http://localhost:8000).
24 | * Please ensure all the examples work correctly after your change. If you're
25 | adding a major new use-case, add a new example demonstrating its use.
26 | * Please **do not** commit the build files. Make sure **only** your changes to
27 | `/src/`, `/less/` and `/examples/src` are included in your PR.
28 | * Be careful to follow the code style of the project. Run `npm run lint` after
29 | your changes and ensure you do not introduce any new errors or warnings.
30 |
31 | * Ensure that your effort is aligned with the project's roadmap by talking to
32 | the maintainers, especially if you are going to spend a lot of time on it.
33 | * Make sure there's an issue open for any work you take on and intend to submit
34 | as a pull request - it helps core members review your concept and direction
35 | early and is a good way to discuss what you're planning to do.
36 | * If you open an issue and are interested in working on a fix, please let us
37 | know. We'll help you get started, rather than adding it to the queue.
38 | * Make sure you do not add regressions by running `npm test`.
39 | * Where possible, include tests with your changes, either that demonstrates the
40 | bug, or tests the new functionality. If you're not sure how to test your
41 | changes, feel free to ping @bruderstein
42 | * Run `npm run cover` to check that the coverage hasn't dropped, and look at the
43 | report (under the generated `coverage` directory) to check that your changes are
44 | covered
45 | * Please [follow our established coding conventions](https://github.com/keystonejs/keystone/wiki/Coding-Standards)
46 | (with regards to formatting, etc)
47 | * You can also run `npm run lint` - our linter is a WIP
48 | but please ensure there are not more violations than before your changes.
49 | * All new features and changes need documentation. We have three translations,
50 | please read our [Documentation Guidelines](https://github.com/keystonejs/keystone/wiki/Documentation-Translation-Guidelines).
51 |
52 | * _Make sure you revert your build before submitting a PR_ to reduce the chance
53 | of conflicts. `npm run build` is run after PRs are merged and before any
54 | releases are made.
55 |
--------------------------------------------------------------------------------
/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ### Thanks for using react-select!
2 |
3 | Before creating an issue...
4 |
5 | # Are you asking a question?
6 | Please don't file GitHub issues to ask questions. Use Stack Overflow with a [#react-select](http://stackoverflow.com/questions/tagged/react-select) tag
7 |
8 |
9 | # Are you reporting a bug or runtime error?
10 | Please include a test case that demonstrates the issue you're reporting!
11 | This is very helpful to maintainers in order to help us see the issue you're seeing.
12 |
13 | Here is a Plunker you can fork that has react-select loaded and supports JSX syntax:
14 | https://plnkr.co/edit/dHygFMWWqVwaRAfpEmbn?p=preview
15 |
16 | You may also find the [online Babel tool](https://babeljs.io/repl/) quite helpful if you wish to use ES6/ES7 syntax not yet supported by the browser you are using.
17 |
18 |
19 | # Are you making a feature request?
20 | Provide as much information as possible about your requested feature. Here are a few questions you may consider answering:
21 |
22 | * What's your use case? (Tell us about your application and what problem you're trying to solve.)
23 | * What interface do you have in mind? (What new properties or methods do you think might be helpful?)
24 | * Can you point to similar functionality with any existing libraries or components? (Working demos can be helpful.)
25 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2017 HubSpot
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 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-select-plus",
3 | "main": [
4 | "dist/react-select-plus.min.js",
5 | "dist/react-select-plus.min.css"
6 | ],
7 | "version": "1.2.0",
8 | "homepage": "https://github.com/HubSpot/react-select-plus",
9 | "authors": [
10 | "Trevor Burnham"
11 | ],
12 | "description": "A Select control built with and for ReactJS",
13 | "moduleType": ["amd", "globals", "node"],
14 | "dependencies": {
15 | "classnames": "^2.2.0",
16 | "react-input-autosize": "^2.0.1"
17 | },
18 | "keywords": [
19 | "react",
20 | "react-component",
21 | "select",
22 | "multiselect",
23 | "combobox",
24 | "input",
25 | "form",
26 | "ui"
27 | ],
28 | "license": "MIT",
29 | "ignore": [
30 | ".editorconfig",
31 | ".gitignore",
32 | "package.json",
33 | "src",
34 | "node_modules",
35 | "example",
36 | "test"
37 | ]
38 | }
39 |
--------------------------------------------------------------------------------
/dist/react-select-plus.min.css:
--------------------------------------------------------------------------------
1 | .Select{position:relative}.Select input::-webkit-contacts-auto-fill-button,.Select input::-webkit-credentials-auto-fill-button{display:none!important}.Select input::-ms-clear{display:none!important}.Select input::-ms-reveal{display:none!important}.Select,.Select div,.Select input,.Select span{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.Select.is-disabled .Select-arrow-zone{cursor:default;pointer-events:none;opacity:.35}.Select.is-disabled>.Select-control{background-color:#f9f9f9}.Select.is-disabled>.Select-control:hover{box-shadow:none}.Select.is-open>.Select-control{border-bottom-right-radius:0;border-bottom-left-radius:0;background:#fff;border-color:#b3b3b3 #ccc #d9d9d9}.Select.is-open>.Select-control .Select-arrow{top:-2px;border-color:transparent transparent #999;border-width:0 5px 5px}.Select.is-searchable.is-open>.Select-control{cursor:text}.Select.is-searchable.is-focused:not(.is-open)>.Select-control{cursor:text}.Select.is-focused>.Select-control{background:#fff}.Select.is-focused:not(.is-open)>.Select-control{border-color:#007eff;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 0 3px rgba(0,126,255,.1);background:#fff}.Select.has-value.is-clearable.Select--single>.Select-control .Select-value{padding-right:42px}.Select.has-value.Select--single>.Select-control .Select-value .Select-value-label,.Select.has-value.is-pseudo-focused.Select--single>.Select-control .Select-value .Select-value-label{color:#333}.Select.has-value.Select--single>.Select-control .Select-value a.Select-value-label,.Select.has-value.is-pseudo-focused.Select--single>.Select-control .Select-value a.Select-value-label{cursor:pointer;text-decoration:none}.Select.has-value.Select--single>.Select-control .Select-value a.Select-value-label:focus,.Select.has-value.Select--single>.Select-control .Select-value a.Select-value-label:hover,.Select.has-value.is-pseudo-focused.Select--single>.Select-control .Select-value a.Select-value-label:focus,.Select.has-value.is-pseudo-focused.Select--single>.Select-control .Select-value a.Select-value-label:hover{color:#007eff;outline:0;text-decoration:underline}.Select.has-value.Select--single>.Select-control .Select-value a.Select-value-label:focus,.Select.has-value.is-pseudo-focused.Select--single>.Select-control .Select-value a.Select-value-label:focus{background:#fff}.Select.has-value.is-pseudo-focused .Select-input{opacity:0}.Select .Select-arrow-zone:hover>.Select-arrow,.Select.is-open .Select-arrow{border-top-color:#666}.Select.Select--rtl{direction:rtl;text-align:right}.Select-control{background-color:#fff;border-color:#d9d9d9 #ccc #b3b3b3;border-radius:4px;border:1px solid #ccc;color:#333;cursor:default;display:table;border-spacing:0;border-collapse:separate;height:36px;outline:0;overflow:hidden;position:relative;width:100%}.Select-control:hover{box-shadow:0 1px 0 rgba(0,0,0,.06)}.Select-control .Select-input:focus{outline:0;background:#fff}.Select--single>.Select-control .Select-value,.Select-placeholder{bottom:0;color:#aaa;left:0;line-height:34px;padding-left:10px;padding-right:10px;position:absolute;right:0;top:0;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.Select-input{height:34px;padding-left:10px;padding-right:10px;vertical-align:middle}.Select-input>input{width:100%;background:none transparent;border:0 none;box-shadow:none;cursor:default;display:inline-block;font-family:inherit;font-size:inherit;margin:0;outline:0;line-height:17px;padding:8px 0 12px;-webkit-appearance:none}.is-focused .Select-input>input{cursor:text}.has-value.is-pseudo-focused .Select-input{opacity:0}.Select-control:not(.is-searchable)>.Select-input{outline:0}.Select-loading-zone{cursor:pointer;display:table-cell;position:relative;text-align:center;vertical-align:middle;width:16px}.Select-loading{-webkit-animation:Select-animation-spin .4s infinite linear;-o-animation:Select-animation-spin .4s infinite linear;animation:Select-animation-spin .4s infinite linear;width:16px;height:16px;box-sizing:border-box;border-radius:50%;border:2px solid #ccc;border-right-color:#333;display:inline-block;position:relative;vertical-align:middle}.Select-clear-zone{-webkit-animation:Select-animation-fadeIn .2s;-o-animation:Select-animation-fadeIn .2s;animation:Select-animation-fadeIn .2s;color:#999;cursor:pointer;display:table-cell;position:relative;text-align:center;vertical-align:middle;width:17px}.Select-clear-zone:hover{color:#D0021B}.Select-clear{display:inline-block;font-size:18px;line-height:1}.Select--multi .Select-clear-zone{width:17px}.Select-arrow-zone{cursor:pointer;display:table-cell;position:relative;text-align:center;vertical-align:middle;width:25px;padding-right:5px}.Select--rtl .Select-arrow-zone{padding-right:0;padding-left:5px}.Select-arrow{border-color:#999 transparent transparent;border-style:solid;border-width:5px 5px 2.5px;display:inline-block;height:0;width:0;position:relative}.Select-control>:last-child{padding-right:5px}.Select--multi .Select-multi-value-wrapper{display:inline-block}.Select .Select-aria-only{position:absolute;display:inline-block;height:1px;width:1px;margin:-1px;clip:rect(0,0,0,0);overflow:hidden;float:left}@-webkit-keyframes Select-animation-fadeIn{from{opacity:0}to{opacity:1}}@keyframes Select-animation-fadeIn{from{opacity:0}to{opacity:1}}.Select-menu-outer{border-bottom-right-radius:4px;border-bottom-left-radius:4px;background-color:#fff;border:1px solid #ccc;border-top-color:#e6e6e6;box-shadow:0 1px 0 rgba(0,0,0,.06);box-sizing:border-box;margin-top:-1px;max-height:200px;position:absolute;top:100%;width:100%;z-index:1;-webkit-overflow-scrolling:touch}.Select-menu{max-height:198px;overflow-y:auto}.Select-option-group-label{box-sizing:border-box;background-color:#fff;color:#666;font-weight:700;cursor:default;display:block;padding:8px 10px}.Select-option-group-label~.Select-option,.Select-option-group-label~.Select-option-group{padding-left:20px}.Select-option{box-sizing:border-box;background-color:#fff;color:#666;cursor:pointer;display:block;padding:8px 10px}.Select-option:last-child{border-bottom-right-radius:4px;border-bottom-left-radius:4px}.Select-option.is-selected{background-color:#f5faff;background-color:rgba(0,126,255,.04);color:#333}.Select-option.is-focused{background-color:#ebf5ff;background-color:rgba(0,126,255,.08);color:#333}.Select-option.is-disabled{color:#ccc;cursor:default}.Select-noresults{box-sizing:border-box;color:#999;cursor:default;display:block;padding:8px 10px}.Select--multi .Select-input{vertical-align:middle;margin-left:10px;padding:0}.Select--multi.Select--rtl .Select-input{margin-left:0;margin-right:10px}.Select--multi.has-value .Select-input{margin-left:5px}.Select--multi .Select-value{background-color:#ebf5ff;background-color:rgba(0,126,255,.08);border-radius:2px;border:1px solid #c2e0ff;border:1px solid rgba(0,126,255,.24);color:#007eff;display:inline-block;font-size:.9em;line-height:1.4;margin-left:5px;margin-top:5px;vertical-align:top}.Select--multi .Select-value-icon,.Select--multi .Select-value-label{display:inline-block;vertical-align:middle}.Select--multi .Select-value-label{border-bottom-right-radius:2px;border-top-right-radius:2px;cursor:default;padding:2px 5px}.Select--multi a.Select-value-label{color:#007eff;cursor:pointer;text-decoration:none}.Select--multi a.Select-value-label:hover{text-decoration:underline}.Select--multi .Select-value-icon{cursor:pointer;border-bottom-left-radius:2px;border-top-left-radius:2px;border-right:1px solid #c2e0ff;border-right:1px solid rgba(0,126,255,.24);padding:1px 5px 3px}.Select--multi .Select-value-icon:focus,.Select--multi .Select-value-icon:hover{background-color:#d8eafd;background-color:rgba(0,113,230,.08);color:#0071e6}.Select--multi .Select-value-icon:active{background-color:#c2e0ff;background-color:rgba(0,126,255,.24)}.Select--multi.Select--rtl .Select-value{margin-left:0;margin-right:5px}.Select--multi.Select--rtl .Select-value-icon{border-right:none;border-left:1px solid #c2e0ff;border-left:1px solid rgba(0,126,255,.24)}.Select--multi.is-disabled .Select-value{background-color:#fcfcfc;border:1px solid #e3e3e3;color:#333}.Select--multi.is-disabled .Select-value-icon{cursor:not-allowed;border-right:1px solid #e3e3e3}.Select--multi.is-disabled .Select-value-icon:active,.Select--multi.is-disabled .Select-value-icon:focus,.Select--multi.is-disabled .Select-value-icon:hover{background-color:#fcfcfc}@keyframes Select-animation-spin{to{transform:rotate(1turn)}}@-webkit-keyframes Select-animation-spin{to{-webkit-transform:rotate(1turn)}}
--------------------------------------------------------------------------------
/examples/dist/.gitignore:
--------------------------------------------------------------------------------
1 | ## This file is here to ensure it is included in the gh-pages branch,
2 | ## when `gulp deploy` is used to push updates to the demo site.
3 |
4 | # Dependency directory
5 | node_modules
6 |
--------------------------------------------------------------------------------
/examples/dist/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | React-Select-Plus Example
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
26 |
34 |
42 |
47 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/examples/dist/standalone.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | React-Select Example
4 |
5 |
6 |
7 |
8 |
React Select
9 |
Standalone bulid
10 |
For usage without babel / browserify / webpack
11 |
12 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
48 |
49 |
--------------------------------------------------------------------------------
/examples/src/.gitignore:
--------------------------------------------------------------------------------
1 | ## This file is here to ensure it is included in the gh-pages branch,
2 | ## when `gulp deploy` is used to push updates to the demo site.
3 |
4 | # Dependency directory
5 | node_modules
6 |
--------------------------------------------------------------------------------
/examples/src/.npmignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HubSpot/react-select-plus/8ae7465fc9b6b9600a01e4e23de97f2d403fd139/examples/src/.npmignore
--------------------------------------------------------------------------------
/examples/src/app.js:
--------------------------------------------------------------------------------
1 | /* eslint react/prop-types: 0 */
2 |
3 | import React from 'react';
4 | import ReactDOM from 'react-dom';
5 | import Select from 'react-select-plus';
6 | import './example.less';
7 |
8 | import Creatable from './components/Creatable';
9 | import Contributors from './components/Contributors';
10 | import GithubUsers from './components/GithubUsers';
11 | import CustomComponents from './components/CustomComponents';
12 | import CustomRender from './components/CustomRender';
13 | import GroupedOptionsField from './components/GroupedOptionsField';
14 | import Multiselect from './components/Multiselect';
15 | import NumericSelect from './components/NumericSelect';
16 | import BooleanSelect from './components/BooleanSelect';
17 | import Virtualized from './components/Virtualized';
18 | import States from './components/States';
19 |
20 | ReactDOM.render(
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
36 |
,
37 | document.getElementById('example')
38 | );
39 |
--------------------------------------------------------------------------------
/examples/src/components/BooleanSelect.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import createClass from 'create-react-class';
3 | import PropTypes from 'prop-types';
4 | import Select from 'react-select-plus';
5 |
6 | var ValuesAsBooleansField = createClass({
7 | displayName: 'ValuesAsBooleansField',
8 | propTypes: {
9 | label: PropTypes.string
10 | },
11 | getInitialState () {
12 | return {
13 | options: [
14 | { value: true, label: 'Yes' },
15 | { value: false, label: 'No' }
16 | ],
17 | value: null
18 | };
19 | },
20 | onChange(value) {
21 | this.setState({ value });
22 | console.log('Boolean Select value changed to', value);
23 | },
24 | render () {
25 | return (
26 |
27 |
{this.props.label} (Source)
28 |
34 |
This example uses simple boolean values
35 |
36 | );
37 | }
38 | });
39 |
40 | module.exports = ValuesAsBooleansField;
41 |
--------------------------------------------------------------------------------
/examples/src/components/Cities.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Select from 'react-select-plus';
3 | import createClass from 'create-react-class';
4 | import PropTypes from 'prop-types';
5 | import { AutoSizer, VirtualScroll } from 'react-virtualized';
6 |
7 | const DATA = require('../data/cities');
8 |
9 | const OPTION_HEIGHT = 35;
10 | const MAX_OPTIONS_HEIGHT = 200;
11 |
12 | var CitiesField = createClass({
13 | displayName: 'CitiesField',
14 | propTypes: {
15 | label: PropTypes.string,
16 | searchable: PropTypes.bool,
17 | },
18 | getDefaultProps () {
19 | return {
20 | label: 'Cities:',
21 | searchable: true,
22 | };
23 | },
24 | getInitialState () {
25 | return {
26 | country: 'AU',
27 | disabled: false,
28 | searchable: this.props.searchable,
29 | selectValue: 'new-south-wales',
30 | clearable: true,
31 | };
32 | },
33 | switchCountry (e) {
34 | var newCountry = e.target.value;
35 | console.log('Country changed to ' + newCountry);
36 | this.setState({
37 | country: newCountry,
38 | selectValue: null
39 | });
40 | },
41 | updateValue (newValue) {
42 | console.log('State changed to ' + newValue);
43 | this.setState({
44 | selectValue: newValue
45 | });
46 | },
47 | focusStateSelect () {
48 | this.refs.stateSelect.focus();
49 | },
50 | toggleCheckbox (e) {
51 | let newState = {};
52 | newState[e.target.name] = e.target.checked;
53 | this.setState(newState);
54 | },
55 | renderMenu({ focusedOption, focusOption, labelKey, options, selectValue, valueArray }) {
56 | const focusedOptionIndex = options.indexOf(focusedOption);
57 | const height = Math.min(MAX_OPTIONS_HEIGHT, options.length * OPTION_HEIGHT);
58 |
59 | return (
60 |
61 | {({ width }) => (
62 | (
67 | focusOption(options[index])}
69 | onClick={() => selectValue(options[index])}
70 | style={{
71 | backgroundColor: options[index] === focusedOption ? '#eee' : '#fff',
72 | height: OPTION_HEIGHT,
73 | display: 'flex',
74 | alignItems: 'center',
75 | padding: '0 .5rem'
76 | }}
77 | >
78 | {options[index][labelKey]}
79 |
80 | )}
81 | rowsCount={options.length}
82 | scrollToIndex={focusedOptionIndex}
83 | width={width}
84 | />
85 | )}
86 |
87 | );
88 | },
89 | render () {
90 | var options = DATA.CITIES;
91 | return (
92 |
93 |
World's Largest Cities
94 | Uses react-virtualized to display data
95 |
110 |
111 | );
112 | }
113 | });
114 |
115 |
116 | module.exports = CitiesField;
117 |
--------------------------------------------------------------------------------
/examples/src/components/Contributors.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import createClass from 'create-react-class';
3 | import PropTypes from 'prop-types';
4 | import Select from 'react-select-plus';
5 |
6 | const CONTRIBUTORS = require('../data/contributors');
7 | const MAX_CONTRIBUTORS = 6;
8 | const ASYNC_DELAY = 500;
9 |
10 | const Contributors = createClass({
11 | displayName: 'Contributors',
12 | propTypes: {
13 | label: PropTypes.string,
14 | },
15 | getInitialState () {
16 | return {
17 | multi: true,
18 | value: [CONTRIBUTORS[0]],
19 | };
20 | },
21 | onChange (value) {
22 | this.setState({
23 | value: value,
24 | });
25 | },
26 | switchToMulti () {
27 | this.setState({
28 | multi: true,
29 | value: [this.state.value],
30 | });
31 | },
32 | switchToSingle () {
33 | this.setState({
34 | multi: false,
35 | value: this.state.value[0],
36 | });
37 | },
38 | getContributors (input, callback) {
39 | input = input.toLowerCase();
40 | var options = CONTRIBUTORS.filter(i => {
41 | return i.github.substr(0, input.length) === input;
42 | });
43 | var data = {
44 | options: options.slice(0, MAX_CONTRIBUTORS),
45 | complete: options.length <= MAX_CONTRIBUTORS,
46 | };
47 | setTimeout(function() {
48 | callback(null, data);
49 | }, ASYNC_DELAY);
50 | },
51 | gotoContributor (value, event) {
52 | window.open('https://github.com/' + value.github);
53 | },
54 | render () {
55 | return (
56 |
57 |
{this.props.label} (Source)
58 |
59 |
60 |
61 |
62 | Multiselect
63 |
64 |
65 |
66 | Single Value
67 |
68 |
69 |
This example implements custom label and value properties, async options and opens the github profiles in a new window when values are clicked
70 |
71 | );
72 | }
73 | });
74 |
75 | module.exports = Contributors;
76 |
--------------------------------------------------------------------------------
/examples/src/components/Creatable.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import createClass from 'create-react-class';
3 | import PropTypes from 'prop-types';
4 | import Select from 'react-select-plus';
5 |
6 | var CreatableDemo = createClass({
7 | displayName: 'CreatableDemo',
8 | propTypes: {
9 | hint: PropTypes.string,
10 | label: PropTypes.string
11 | },
12 | getInitialState () {
13 | return {
14 | multi: true,
15 | multiValue: [],
16 | options: [
17 | { value: 'R', label: 'Red' },
18 | { value: 'G', label: 'Green' },
19 | { value: 'B', label: 'Blue' }
20 | ],
21 | value: undefined
22 | };
23 | },
24 | handleOnChange (value) {
25 | const { multi } = this.state;
26 | if (multi) {
27 | this.setState({ multiValue: value });
28 | } else {
29 | this.setState({ value });
30 | }
31 | },
32 | render () {
33 | const { multi, multiValue, options, value } = this.state;
34 | return (
35 |
36 |
{this.props.label} (Source)
37 |
43 |
{this.props.hint}
44 |
45 |
46 | this.setState({ multi: true })}
51 | />
52 | Multiselect
53 |
54 |
55 | this.setState({ multi: false })}
60 | />
61 | Single Value
62 |
63 |
64 |
65 | );
66 | }
67 | });
68 |
69 | module.exports = CreatableDemo;
70 |
--------------------------------------------------------------------------------
/examples/src/components/CustomComponents.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import createClass from 'create-react-class';
3 | import PropTypes from 'prop-types';
4 | import Select from 'react-select-plus';
5 | import Gravatar from 'react-gravatar';
6 |
7 | const USERS = require('../data/users');
8 | const GRAVATAR_SIZE = 15;
9 |
10 | const stringOrNode = PropTypes.oneOfType([
11 | PropTypes.string,
12 | PropTypes.node,
13 | ]);
14 |
15 | const GravatarOption = createClass({
16 | propTypes: {
17 | children: PropTypes.node,
18 | className: PropTypes.string,
19 | isDisabled: PropTypes.bool,
20 | isFocused: PropTypes.bool,
21 | isSelected: PropTypes.bool,
22 | onFocus: PropTypes.func,
23 | onSelect: PropTypes.func,
24 | option: PropTypes.object.isRequired,
25 | },
26 | handleMouseDown (event) {
27 | event.preventDefault();
28 | event.stopPropagation();
29 | this.props.onSelect(this.props.option, event);
30 | },
31 | handleMouseEnter (event) {
32 | this.props.onFocus(this.props.option, event);
33 | },
34 | handleMouseMove (event) {
35 | if (this.props.isFocused) return;
36 | this.props.onFocus(this.props.option, event);
37 | },
38 | render () {
39 | let gravatarStyle = {
40 | borderRadius: 3,
41 | display: 'inline-block',
42 | marginRight: 10,
43 | position: 'relative',
44 | top: -2,
45 | verticalAlign: 'middle',
46 | };
47 | return (
48 |
53 |
54 | {this.props.children}
55 |
56 | );
57 | }
58 | });
59 |
60 | const GravatarValue = createClass({
61 | propTypes: {
62 | children: PropTypes.node,
63 | placeholder: stringOrNode,
64 | value: PropTypes.object
65 | },
66 | render () {
67 | var gravatarStyle = {
68 | borderRadius: 3,
69 | display: 'inline-block',
70 | marginRight: 10,
71 | position: 'relative',
72 | top: -2,
73 | verticalAlign: 'middle',
74 | };
75 | return (
76 |
77 |
78 |
79 | {this.props.children}
80 |
81 |
82 | );
83 | }
84 | });
85 |
86 | const UsersField = createClass({
87 | propTypes: {
88 | hint: PropTypes.string,
89 | label: PropTypes.string,
90 | },
91 | getInitialState () {
92 | return {};
93 | },
94 | setValue (value) {
95 | this.setState({ value });
96 | },
97 | render () {
98 | var placeholder = ☺ Select User ;
99 |
100 | return (
101 |
102 |
{this.props.label} (Source)
103 |
112 |
113 | This example implements custom Option and Value components to render a Gravatar image for each user based on their email.
114 | It also demonstrates rendering HTML elements as the placeholder.
115 |
116 |
117 | );
118 | }
119 | });
120 |
121 | function arrowRenderer () {
122 | return (
123 | +
124 | );
125 | }
126 |
127 | module.exports = UsersField;
128 |
--------------------------------------------------------------------------------
/examples/src/components/CustomRender.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import createClass from 'create-react-class';
3 | import PropTypes from 'prop-types';
4 | import Select from 'react-select-plus';
5 | import Highlighter from 'react-highlight-words';
6 |
7 | var DisabledUpsellOptions = createClass({
8 | displayName: 'DisabledUpsellOptions',
9 | propTypes: {
10 | label: PropTypes.string,
11 | },
12 | getInitialState () {
13 | return {};
14 | },
15 | setValue (value) {
16 | this.setState({ value });
17 | if (value) {
18 | console.log('Support level selected:', value.label);
19 | }
20 | },
21 | renderLink: function() {
22 | return Upgrade here! ;
23 | },
24 | renderOption: function(option) {
25 | return (
26 |
30 | );
31 | },
32 | renderValue: function(option) {
33 | return {option.label} ;
34 | },
35 | render: function() {
36 | var options = [
37 | { label: 'Basic customer support', value: 'basic', color: '#E31864' },
38 | { label: 'Premium customer support', value: 'premium', color: '#6216A3' },
39 | { label: 'Pro customer support', value: 'pro', disabled: true, link: this.renderLink() },
40 | ];
41 | return (
42 |
43 |
{this.props.label} (Source)
44 |
this._inputValue = inputValue}
46 | options={options}
47 | optionRenderer={this.renderOption}
48 | onChange={this.setValue}
49 | value={this.state.value}
50 | valueRenderer={this.renderValue}
51 | />
52 | This demonstrates custom render methods and links in disabled options
53 |
54 | );
55 | }
56 | });
57 | module.exports = DisabledUpsellOptions;
58 |
--------------------------------------------------------------------------------
/examples/src/components/GithubUsers.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import createClass from 'create-react-class';
3 | import PropTypes from 'prop-types';
4 | import Select from 'react-select-plus';
5 | import fetch from 'isomorphic-fetch';
6 |
7 |
8 | const GithubUsers = createClass({
9 | displayName: 'GithubUsers',
10 | propTypes: {
11 | label: PropTypes.string,
12 | },
13 | getInitialState () {
14 | return {
15 | backspaceRemoves: true,
16 | multi: true,
17 | creatable: false,
18 | };
19 | },
20 | onChange (value) {
21 | this.setState({
22 | value: value,
23 | });
24 | },
25 | switchToMulti () {
26 | this.setState({
27 | multi: true,
28 | value: [this.state.value],
29 | });
30 | },
31 | switchToSingle () {
32 | this.setState({
33 | multi: false,
34 | value: this.state.value ? this.state.value[0] : null
35 | });
36 | },
37 | getUsers (input) {
38 | if (!input) {
39 | return Promise.resolve({ options: [] });
40 | }
41 |
42 | return fetch(`https://api.github.com/search/users?q=${input}`)
43 | .then((response) => response.json())
44 | .then((json) => {
45 | return { options: json.items };
46 | });
47 | },
48 | gotoUser (value, event) {
49 | window.open(value.html_url);
50 | },
51 | toggleBackspaceRemoves () {
52 | this.setState({
53 | backspaceRemoves: !this.state.backspaceRemoves
54 | });
55 | },
56 | toggleCreatable () {
57 | this.setState({
58 | creatable: !this.state.creatable
59 | });
60 | },
61 | render () {
62 | const AsyncComponent = this.state.creatable
63 | ? Select.AsyncCreatable
64 | : Select.Async;
65 |
66 | return (
67 |
92 | );
93 | }
94 | });
95 |
96 | module.exports = GithubUsers;
97 |
--------------------------------------------------------------------------------
/examples/src/components/GroupedOptionsField.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import createClass from 'create-react-class';
3 | import PropTypes from 'prop-types';
4 | import Select from 'react-select-plus';
5 |
6 | var ops = [{
7 | label: 'Black',
8 | value: 'black',
9 | }, {
10 | label: 'Primary Colors',
11 | options: [{
12 | label: 'Yellow',
13 | value: 'yellow'
14 | }, {
15 | label: 'Red',
16 | value: 'red'
17 | }, {
18 | label: 'Blue',
19 | value: 'blue'
20 | }]
21 | }, {
22 | label: 'Secondary Colors',
23 | options: [{
24 | label: 'Orange',
25 | value: 'orange'
26 | }, {
27 | label: 'Purple',
28 | options: [{
29 | label: 'Light Purple',
30 | value: 'light_purple'
31 | }, {
32 | label: 'Medium Purple',
33 | value: 'medium_purple'
34 | }, {
35 | label: 'Dark Purple',
36 | value: 'dark_purple'
37 | }]
38 | }, {
39 | label: 'Green',
40 | value: 'green'
41 | }]
42 | }, {
43 | label: 'White',
44 | value: 'white',
45 | }];
46 |
47 | var GroupedOptionsField = createClass({
48 | displayName: 'GroupedOptionsField',
49 | propTypes: {
50 | delimiter: PropTypes.string,
51 | label: PropTypes.string,
52 | multi: PropTypes.bool,
53 | },
54 |
55 | getInitialState () {
56 | return {
57 | value: null,
58 | };
59 | },
60 |
61 | onChange(value) {
62 | this.setState({ value });
63 | console.log('Option Groups Select value changed to', value);
64 | },
65 |
66 | render () {
67 | return (
68 |
69 |
{this.props.label}
70 |
75 |
76 | );
77 | }
78 | });
79 |
80 | module.exports = GroupedOptionsField;
81 |
--------------------------------------------------------------------------------
/examples/src/components/Multiselect.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import createClass from 'create-react-class';
3 | import PropTypes from 'prop-types';
4 | import Select from 'react-select-plus';
5 |
6 | const FLAVOURS = [
7 | { label: 'Chocolate', value: 'chocolate' },
8 | { label: 'Vanilla', value: 'vanilla' },
9 | { label: 'Strawberry', value: 'strawberry' },
10 | { label: 'Caramel', value: 'caramel' },
11 | { label: 'Cookies and Cream', value: 'cookiescream' },
12 | { label: 'Peppermint', value: 'peppermint' },
13 | ];
14 |
15 | const WHY_WOULD_YOU = [
16 | { label: 'Chocolate (are you crazy?)', value: 'chocolate', disabled: true },
17 | ].concat(FLAVOURS.slice(1));
18 |
19 | var MultiSelectField = createClass({
20 | displayName: 'MultiSelectField',
21 | propTypes: {
22 | label: PropTypes.string,
23 | },
24 | getInitialState () {
25 | return {
26 | removeSelected: true,
27 | disabled: false,
28 | crazy: false,
29 | stayOpen: false,
30 | value: [],
31 | rtl: false,
32 | };
33 | },
34 | handleSelectChange (value) {
35 | console.log('You\'ve selected:', value);
36 | this.setState({ value });
37 | },
38 | toggleCheckbox (e) {
39 | this.setState({
40 | [e.target.name]: e.target.checked,
41 | });
42 | },
43 | toggleRtl (e) {
44 | let rtl = e.target.checked;
45 | this.setState({ rtl });
46 | },
47 |
48 | render () {
49 | const { crazy, disabled, stayOpen, value } = this.state;
50 | const options = crazy ? WHY_WOULD_YOU : FLAVOURS;
51 | return (
52 |
90 | );
91 | }
92 | });
93 |
94 | module.exports = MultiSelectField;
95 |
--------------------------------------------------------------------------------
/examples/src/components/NumericSelect.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import createClass from 'create-react-class';
3 | import PropTypes from 'prop-types';
4 | import Select from 'react-select-plus';
5 |
6 | var ValuesAsNumbersField = createClass({
7 | displayName: 'ValuesAsNumbersField',
8 | propTypes: {
9 | label: PropTypes.string
10 | },
11 | getInitialState () {
12 | return {
13 | options: [
14 | { value: 10, label: 'Ten' },
15 | { value: 11, label: 'Eleven' },
16 | { value: 12, label: 'Twelve' },
17 | { value: 23, label: 'Twenty-three' },
18 | { value: 24, label: 'Twenty-four' }
19 | ],
20 | matchPos: 'any',
21 | matchValue: true,
22 | matchLabel: true,
23 | value: null,
24 | multi: false
25 | };
26 | },
27 | onChangeMatchStart(event) {
28 | this.setState({
29 | matchPos: event.target.checked ? 'start' : 'any'
30 | });
31 | },
32 | onChangeMatchValue(event) {
33 | this.setState({
34 | matchValue: event.target.checked
35 | });
36 | },
37 | onChangeMatchLabel(event) {
38 | this.setState({
39 | matchLabel: event.target.checked
40 | });
41 | },
42 | onChange(value) {
43 | this.setState({ value });
44 | console.log('Numeric Select value changed to', value);
45 | },
46 | onChangeMulti(event) {
47 | this.setState({
48 | multi: event.target.checked
49 | });
50 | },
51 | render () {
52 | var matchProp = 'any';
53 | if (this.state.matchLabel && !this.state.matchValue) {
54 | matchProp = 'label';
55 | }
56 | if (!this.state.matchLabel && this.state.matchValue) {
57 | matchProp = 'value';
58 | }
59 | return (
60 |
91 | );
92 | }
93 | });
94 |
95 | module.exports = ValuesAsNumbersField;
96 |
--------------------------------------------------------------------------------
/examples/src/components/States.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import createClass from 'create-react-class';
3 | import PropTypes from 'prop-types';
4 | import Select from 'react-select-plus';
5 |
6 | const STATES = require('../data/states');
7 |
8 | var StatesField = createClass({
9 | displayName: 'StatesField',
10 | propTypes: {
11 | label: PropTypes.string,
12 | searchable: PropTypes.bool,
13 | },
14 | getDefaultProps () {
15 | return {
16 | label: 'States:',
17 | searchable: true,
18 | };
19 | },
20 | getInitialState () {
21 | return {
22 | country: 'AU',
23 | disabled: false,
24 | searchable: this.props.searchable,
25 | selectValue: 'new-south-wales',
26 | clearable: true,
27 | rtl: false,
28 | };
29 | },
30 | clearValue (e) {
31 | this.select.setInputValue('');
32 | },
33 | switchCountry (e) {
34 | var newCountry = e.target.value;
35 | this.setState({
36 | country: newCountry,
37 | selectValue: null,
38 | });
39 | },
40 | updateValue (newValue) {
41 | this.setState({
42 | selectValue: newValue,
43 | });
44 | },
45 | focusStateSelect () {
46 | this.refs.stateSelect.focus();
47 | },
48 | toggleCheckbox (e) {
49 | let newState = {};
50 | newState[e.target.name] = e.target.checked;
51 | this.setState(newState);
52 | },
53 | render () {
54 | var options = STATES[this.state.country];
55 | return (
56 |
108 | );
109 | }
110 | });
111 |
112 |
113 | module.exports = StatesField;
114 |
--------------------------------------------------------------------------------
/examples/src/components/Virtualized.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import createClass from 'create-react-class';
3 | import VirtualizedSelect from 'react-virtualized-select';
4 |
5 | const DATA = require('../data/cities');
6 |
7 | var CitiesField = createClass({
8 | displayName: 'CitiesField',
9 | getInitialState () {
10 | return {};
11 | },
12 | updateValue (newValue) {
13 | this.setState({
14 | selectValue: newValue
15 | });
16 | },
17 | render () {
18 | var options = DATA.CITIES;
19 | return (
20 |
21 |
Cities (Large Dataset) (Source)
22 |
33 |
36 |
37 | );
38 | }
39 | });
40 |
41 |
42 | module.exports = CitiesField;
43 |
--------------------------------------------------------------------------------
/examples/src/data/contributors.js:
--------------------------------------------------------------------------------
1 | module.exports = [
2 | { github: 'jedwatson', name: 'Jed Watson' },
3 | { github: 'bruderstein', name: 'Dave Brotherstone' },
4 | { github: 'jossmac', name: 'Joss Mackison' },
5 | { github: 'jniechcial', name: 'Jakub Niechciał' },
6 | { github: 'craigdallimore', name: 'Craig Dallimore' },
7 | { github: 'julen', name: 'Julen Ruiz Aizpuru' },
8 | { github: 'dcousens', name: 'Daniel Cousens' },
9 | { github: 'jgautsch', name: 'Jon Gautsch' },
10 | { github: 'dmitry-smirnov', name: 'Dmitry Smirnov' },
11 | { github: 'trevorburnham', name: 'Trevor Burnham' },
12 | ];
13 |
--------------------------------------------------------------------------------
/examples/src/data/states.js:
--------------------------------------------------------------------------------
1 | exports.AU = [
2 | { value: 'australian-capital-territory', label: 'Australian Capital Territory', className: 'State-ACT' },
3 | { value: 'new-south-wales', label: 'New South Wales', className: 'State-NSW' },
4 | { value: 'victoria', label: 'Victoria', className: 'State-Vic' },
5 | { value: 'queensland', label: 'Queensland', className: 'State-Qld' },
6 | { value: 'western-australia', label: 'Western Australia', className: 'State-WA' },
7 | { value: 'south-australia', label: 'South Australia', className: 'State-SA' },
8 | { value: 'tasmania', label: 'Tasmania', className: 'State-Tas' },
9 | { value: 'northern-territory', label: 'Northern Territory', className: 'State-NT' },
10 | ];
11 |
12 | exports.US = [
13 | { value: 'AL', label: 'Alabama', disabled: true },
14 | { value: 'AK', label: 'Alaska' },
15 | { value: 'AS', label: 'American Samoa' },
16 | { value: 'AZ', label: 'Arizona' },
17 | { value: 'AR', label: 'Arkansas' },
18 | { value: 'CA', label: 'California' },
19 | { value: 'CO', label: 'Colorado' },
20 | { value: 'CT', label: 'Connecticut' },
21 | { value: 'DE', label: 'Delaware' },
22 | { value: 'DC', label: 'District Of Columbia' },
23 | { value: 'FM', label: 'Federated States Of Micronesia' },
24 | { value: 'FL', label: 'Florida' },
25 | { value: 'GA', label: 'Georgia' },
26 | { value: 'GU', label: 'Guam' },
27 | { value: 'HI', label: 'Hawaii' },
28 | { value: 'ID', label: 'Idaho' },
29 | { value: 'IL', label: 'Illinois' },
30 | { value: 'IN', label: 'Indiana' },
31 | { value: 'IA', label: 'Iowa' },
32 | { value: 'KS', label: 'Kansas' },
33 | { value: 'KY', label: 'Kentucky' },
34 | { value: 'LA', label: 'Louisiana' },
35 | { value: 'ME', label: 'Maine' },
36 | { value: 'MH', label: 'Marshall Islands' },
37 | { value: 'MD', label: 'Maryland' },
38 | { value: 'MA', label: 'Massachusetts' },
39 | { value: 'MI', label: 'Michigan' },
40 | { value: 'MN', label: 'Minnesota' },
41 | { value: 'MS', label: 'Mississippi' },
42 | { value: 'MO', label: 'Missouri' },
43 | { value: 'MT', label: 'Montana' },
44 | { value: 'NE', label: 'Nebraska' },
45 | { value: 'NV', label: 'Nevada' },
46 | { value: 'NH', label: 'New Hampshire' },
47 | { value: 'NJ', label: 'New Jersey' },
48 | { value: 'NM', label: 'New Mexico' },
49 | { value: 'NY', label: 'New York' },
50 | { value: 'NC', label: 'North Carolina' },
51 | { value: 'ND', label: 'North Dakota' },
52 | { value: 'MP', label: 'Northern Mariana Islands' },
53 | { value: 'OH', label: 'Ohio' },
54 | { value: 'OK', label: 'Oklahoma' },
55 | { value: 'OR', label: 'Oregon' },
56 | { value: 'PW', label: 'Palau' },
57 | { value: 'PA', label: 'Pennsylvania' },
58 | { value: 'PR', label: 'Puerto Rico' },
59 | { value: 'RI', label: 'Rhode Island' },
60 | { value: 'SC', label: 'South Carolina' },
61 | { value: 'SD', label: 'South Dakota' },
62 | { value: 'TN', label: 'Tennessee' },
63 | { value: 'TX', label: 'Texas' },
64 | { value: 'UT', label: 'Utah' },
65 | { value: 'VT', label: 'Vermont' },
66 | { value: 'VI', label: 'Virgin Islands' },
67 | { value: 'VA', label: 'Virginia' },
68 | { value: 'WA', label: 'Washington' },
69 | { value: 'WV', label: 'West Virginia' },
70 | { value: 'WI', label: 'Wisconsin' },
71 | { value: 'WY', label: 'Wyoming' },
72 | ];
73 |
--------------------------------------------------------------------------------
/examples/src/data/users.js:
--------------------------------------------------------------------------------
1 | module.exports = [
2 | { value: 'John Smith', label: 'John Smith', email: 'john@smith.com' },
3 | { value: 'Merry Jane', label: 'Merry Jane', email: 'merry@jane.com' },
4 | { value: 'Stan Hoper', label: 'Stan Hoper', email: 'stan@hoper.com' }
5 | ];
6 |
--------------------------------------------------------------------------------
/examples/src/example.less:
--------------------------------------------------------------------------------
1 | //
2 | // Common Example Styles
3 | // ------------------------------
4 |
5 |
6 |
7 |
8 | // import select field styles
9 |
10 | @import "../../less/select.less";
11 |
12 |
13 |
14 |
15 | // Constants
16 | // ------------------------------
17 |
18 | // example site
19 |
20 | @heading-color: black;
21 | @text-color: #333;
22 | @link-color: #007eff;
23 | @gutter: 30px;
24 |
25 |
26 |
27 |
28 | // Base
29 | // ------------------------------
30 |
31 | body {
32 | color: @text-color;
33 | font-family: Helvetica Neue, Helvetica, Arial, sans-serif;
34 | font-size: 14px;
35 | line-height: 1.4;
36 | margin: 0;
37 | padding: 0;
38 | }
39 |
40 | a {
41 | color: @link-color;
42 | text-decoration: none;
43 |
44 | &:hover {
45 | text-decoration: underline;
46 | }
47 | }
48 |
49 | .container {
50 | margin-left: auto;
51 | margin-right: auto;
52 | max-width: 400px;
53 | padding: 0 @gutter;
54 | }
55 |
56 |
57 |
58 |
59 | // Headings
60 | // ------------------------------
61 |
62 | h1, h2, h3, h4, h5, h6,
63 | .h1, .h2, .h3, .h4, .h5, .h6 {
64 | color: @heading-color;
65 | font-weight: 500;
66 | line-height: 1;
67 | margin-bottom: .66em;
68 | margin-top: 0;
69 | }
70 |
71 | h1, .h1 {
72 | font-size: 2.4em;
73 | }
74 | h2, .h2 {
75 | font-size: 2em;
76 | font-weight: 300;
77 | }
78 | h3, .h3 {
79 | font-size: 1.25em;
80 | // text-transform: uppercase;
81 | }
82 | h4, .h4 {
83 | font-size: 1em;
84 | }
85 | h5, .h5 {
86 | font-size: .85em;
87 | }
88 | h6, .h6 {
89 | font-size: .75em;
90 | }
91 |
92 |
93 |
94 |
95 | // Layout
96 | // ------------------------------
97 |
98 | // common
99 |
100 | .page-body,
101 | .page-footer,
102 | .page-header {
103 | padding: @gutter 0;
104 | }
105 |
106 | // header
107 |
108 | .page-header {
109 | background-color: @link-color;
110 | color: mix(white, @link-color, 75%);
111 |
112 | h1, h2, h3 {
113 | color: white;
114 | }
115 | p {
116 | font-size: 1.2em;
117 | margin: 0;
118 | }
119 | a {
120 | border-bottom: 1px solid fade(white, 30%);
121 | color: white;
122 | text-decoration: none;
123 |
124 | &:hover,
125 | &:focus {
126 | border-bottom-color: white;
127 | outline: none;
128 | text-decoration: none;
129 | }
130 | }
131 | }
132 |
133 | // subheader
134 |
135 | .page-subheader {
136 | background-color: mix(@link-color, white, 10%);
137 | line-height: 20px;
138 | padding: @gutter 0;
139 | }
140 | .page-subheader__button {
141 | float: right;
142 | }
143 | .page-subheader__link {
144 | border-bottom: 1px solid fade(@link-color, 30%);
145 | outline: none;
146 | text-decoration: none;
147 |
148 | &:hover,
149 | &:focus {
150 | border-bottom-color: @link-color;
151 | outline: none;
152 | text-decoration: none;
153 | }
154 | }
155 |
156 | // footer
157 |
158 | .page-footer {
159 | background-color: #fafafa;
160 | // border-top: 1px solid #eee;
161 | color: #999;
162 | padding: @gutter 0;
163 | text-align: center;
164 |
165 | a {
166 | color: black;
167 | }
168 | }
169 |
170 | // layout changes based on screen dimensions
171 |
172 | @media (min-width: 480px) {
173 | .page-body,
174 | .page-header {
175 | padding: (@gutter * 2) 0;
176 | }
177 | .page-header {
178 | font-size: 1.4em;
179 | }
180 | .page-subheader {
181 | font-size: 1.125em;
182 | line-height: 28px;
183 | }
184 | }
185 |
186 |
187 |
188 |
189 | // Form Styles
190 | // ------------------------------
191 |
192 | .checkbox-list {
193 | margin-top: .5em;
194 | overflow: hidden;
195 | }
196 | .checkbox-list > .checkbox {
197 | clear: left;
198 | float: left;
199 | margin-top: .5em;
200 | }
201 | .checkbox-control {
202 | margin-right: .5em;
203 | position: relative;
204 | top: -1px;
205 | }
206 | .checkbox-label {}
207 |
208 |
209 |
210 |
211 | // Switcher
212 | // ------------------------------
213 |
214 | .switcher {
215 | color: #999;
216 | cursor: default;
217 | font-size: 12px;
218 | margin: 10px 0;
219 | text-transform: uppercase;
220 |
221 | .link {
222 | color: @link-color;
223 | cursor: pointer;
224 | font-weight: bold;
225 | margin-left: 10px;
226 |
227 | &:hover {
228 | text-decoration: underline;
229 | }
230 | }
231 | .active {
232 | color: #666;
233 | font-weight: bold;
234 | margin-left: 10px;
235 | }
236 | }
237 |
238 |
239 |
240 |
241 | // Miscellaneous
242 | // ------------------------------
243 |
244 | .section {
245 | margin-bottom: 40px;
246 | }
247 |
248 | .hint {
249 | font-size: .85em;
250 | margin: 15px 0;
251 | color: #666;
252 | }
253 |
254 | .virtual-scroll {
255 | z-index: 1;
256 | }
257 |
--------------------------------------------------------------------------------
/examples/src/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HubSpot/react-select-plus/8ae7465fc9b6b9600a01e4e23de97f2d403fd139/examples/src/favicon.ico
--------------------------------------------------------------------------------
/examples/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | React-Select-Plus Example
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
26 |
34 |
42 |
47 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/examples/src/standalone.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | react-select umd example
4 |
5 |
6 |
7 |
8 |
React Select
9 |
Standalone (UMD) Bulid
10 |
For usage without browserify / rollup / webpack
11 |
12 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
44 |
45 |
--------------------------------------------------------------------------------
/less/control.less:
--------------------------------------------------------------------------------
1 | //
2 | // Control
3 | // ------------------------------
4 |
5 | // Mixins
6 |
7 | // focused styles
8 | .Select-focus-state(@color) {
9 | border-color: @color;
10 | box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 0 3px fade(@color, 10%);
11 | background: @select-input-bg-focus;
12 | }
13 | // "classic" focused styles: maintain for legacy
14 | .Select-focus-state-classic() {
15 | border-color: @select-input-border-focus lighten(@select-input-border-focus, 5%) lighten(@select-input-border-focus, 5%);
16 | box-shadow: @select-input-box-shadow-focus;
17 | background: @select-input-bg-focus;
18 | }
19 |
20 | // base
21 |
22 | .Select {
23 | position: relative;
24 |
25 | // disable some browser-specific behaviours that break the input
26 | input::-webkit-contacts-auto-fill-button,
27 | input::-webkit-credentials-auto-fill-button {
28 | display: none !important;
29 | }
30 | input::-ms-clear {
31 | display: none !important;
32 | }
33 | input::-ms-reveal {
34 | display: none !important;
35 | }
36 |
37 | // preferred box model
38 | &,
39 | & div,
40 | & input,
41 | & span {
42 | .box-sizing(border-box);
43 | }
44 |
45 | // handle disabled state
46 | &.is-disabled {
47 | .Select-arrow-zone {
48 | cursor: default;
49 | pointer-events: none;
50 | opacity: 0.35;
51 | }
52 |
53 | > .Select-control {
54 | background-color: @select-input-bg-disabled;
55 | &:hover {
56 | box-shadow: none;
57 | }
58 | }
59 | }
60 |
61 | &.is-open > .Select-control {
62 | .border-bottom-radius( 0 );
63 | background: @select-input-bg;
64 | border-color: darken(@select-input-border-color, 10%) @select-input-border-color lighten(@select-input-border-color, 5%);
65 |
66 | // flip the arrow so its pointing up when the menu is open
67 | .Select-arrow {
68 | top: -2px;
69 | border-color: transparent transparent @select-arrow-color;
70 | border-width: 0 @select-arrow-width @select-arrow-width;
71 | }
72 | }
73 |
74 | &.is-searchable {
75 | &.is-open > .Select-control {
76 | cursor: text;
77 | }
78 |
79 | &.is-focused:not(.is-open) > .Select-control {
80 | cursor: text;
81 | }
82 | }
83 |
84 | &.is-focused > .Select-control {
85 | background: @select-input-bg-focus;
86 | }
87 |
88 | &.is-focused:not(.is-open) > .Select-control {
89 | .Select-focus-state(@select-input-border-focus);
90 | }
91 |
92 | &.has-value.is-clearable.Select--single > .Select-control .Select-value {
93 | padding-right: (@select-clear-width + @select-arrow-width * 5);
94 | }
95 |
96 | &.has-value.Select--single > .Select-control .Select-value,
97 | &.has-value.is-pseudo-focused.Select--single > .Select-control .Select-value {
98 | .Select-value-label {
99 | color: @select-text-color;
100 | }
101 | a.Select-value-label {
102 | cursor: pointer;
103 | text-decoration: none;
104 |
105 | &:hover,
106 | &:focus {
107 | color: @select-link-hover-color;
108 | outline: none;
109 | text-decoration: underline;
110 | }
111 |
112 | &:focus {
113 | background: @select-input-bg-focus;
114 | }
115 | }
116 | }
117 |
118 | // fake-hide the input when the control is pseudo-focused
119 | &.has-value.is-pseudo-focused .Select-input {
120 | opacity: 0;
121 | }
122 |
123 | &.is-open .Select-arrow,
124 | .Select-arrow-zone:hover > .Select-arrow {
125 | border-top-color: @select-arrow-color-hover;
126 | }
127 | &.Select--rtl {
128 | direction: rtl;
129 | text-align: right;
130 | }
131 | }
132 |
133 | // base
134 |
135 | .Select-control {
136 | background-color: @select-input-bg;
137 | border-color: lighten(@select-input-border-color, 5%) @select-input-border-color darken(@select-input-border-color, 10%);
138 | border-radius: @select-input-border-radius;
139 | border: @select-input-border-width solid @select-input-border-color;
140 | color: @select-text-color;
141 | cursor: default;
142 | display: table;
143 | border-spacing: 0;
144 | border-collapse: separate;
145 | height: @select-input-height;
146 | outline: none;
147 | overflow: hidden;
148 | position: relative;
149 | width: 100%;
150 |
151 | &:hover {
152 | box-shadow: @select-input-hover-box-shadow;
153 | }
154 |
155 | .Select-input:focus {
156 | outline: none;
157 | background: @select-input-bg-focus;
158 | }
159 | }
160 |
161 | // placeholder
162 | .Select-placeholder,
163 | .Select--single > .Select-control .Select-value {
164 | bottom: 0;
165 | color: @select-input-placeholder;
166 | left: 0;
167 | line-height: @select-input-internal-height;
168 | padding-left: @select-padding-horizontal;
169 | padding-right: @select-padding-horizontal;
170 | position: absolute;
171 | right: 0;
172 | top: 0;
173 |
174 | // crop text
175 | max-width: 100%;
176 | overflow: hidden;
177 | text-overflow: ellipsis;
178 | white-space: nowrap;
179 | }
180 |
181 | // the element users type in
182 |
183 | .Select-input {
184 | // inherits `display: inline-block` from "react-input-autosize"
185 | height: @select-input-internal-height;
186 | padding-left: @select-padding-horizontal;
187 | padding-right: @select-padding-horizontal;
188 | vertical-align: middle;
189 |
190 | > input {
191 | width: 100%;
192 | background: none transparent;
193 | border: 0 none;
194 | box-shadow: none;
195 | cursor: default;
196 | display: inline-block;
197 | font-family: inherit;
198 | font-size: inherit;
199 | // height: @select-input-internal-height;
200 | margin: 0;
201 | outline: none;
202 | // padding: 0;
203 | line-height: 17px; /* For IE 8 compatibility */
204 | padding: ((@select-input-internal-height - 14) / 2 - 2) 0 ((@select-input-internal-height - 14) / 2 + 2); /* For IE 8 compatibility */
205 | -webkit-appearance: none;
206 |
207 | .is-focused & {
208 | cursor: text;
209 | }
210 | }
211 |
212 | }
213 |
214 | // fake-hide the input when the control is pseudo-focused
215 | .has-value.is-pseudo-focused .Select-input {
216 | opacity: 0;
217 | }
218 |
219 | // fake input
220 | .Select-control:not(.is-searchable) > .Select-input {
221 | outline: none;
222 | }
223 |
224 | // loading indicator
225 | .Select-loading-zone {
226 | cursor: pointer;
227 | display: table-cell;
228 | position: relative;
229 | text-align: center;
230 | vertical-align: middle;
231 | width: @select-loading-size;
232 | }
233 | .Select-loading {
234 | .Select-spinner(@select-loading-size, @select-loading-color-bg, @select-loading-color);
235 | vertical-align: middle;
236 | }
237 |
238 |
239 | // the little cross that clears the field
240 |
241 | .Select-clear-zone {
242 | .animation( Select-animation-fadeIn 200ms );
243 | color: @select-clear-color;
244 | cursor: pointer;
245 | display: table-cell;
246 | position: relative;
247 | text-align: center;
248 | vertical-align: middle;
249 | width: @select-clear-width;
250 |
251 | &:hover {
252 | color: @select-clear-hover-color;
253 | }
254 | }
255 | .Select-clear {
256 | display: inline-block;
257 | font-size: @select-clear-size;
258 | line-height: 1;
259 | }
260 | .Select--multi .Select-clear-zone {
261 | width: @select-clear-width;
262 | }
263 |
264 |
265 | // arrow indicator
266 |
267 | .Select-arrow-zone {
268 | cursor: pointer;
269 | display: table-cell;
270 | position: relative;
271 | text-align: center;
272 | vertical-align: middle;
273 | width: (@select-arrow-width * 5);
274 | padding-right: @select-arrow-width;
275 |
276 | .Select--rtl & {
277 | padding-right: 0;
278 | padding-left: @select-arrow-width;
279 | }
280 | }
281 |
282 | .Select-arrow {
283 | border-color: @select-arrow-color transparent transparent;
284 | border-style: solid;
285 | border-width: @select-arrow-width @select-arrow-width (@select-arrow-width / 2);
286 | display: inline-block;
287 | height: 0;
288 | width: 0;
289 | position: relative;
290 | }
291 |
292 | .Select-control > *:last-child {
293 | padding-right: @select-right-padding;
294 | }
295 |
296 | .Select--multi .Select-multi-value-wrapper {
297 | display: inline-block;
298 | }
299 | .Select .Select-aria-only {
300 | position: absolute;
301 | display: inline-block;
302 | height: 1px;
303 | width: 1px;
304 | margin: -1px;
305 | clip: rect(0,0,0,0);
306 | overflow: hidden;
307 | float: left;
308 | }
309 |
310 | // Animation
311 | // ------------------------------
312 |
313 | // fade in
314 |
315 | @-webkit-keyframes Select-animation-fadeIn {
316 | from { opacity: 0; }
317 | to { opacity: 1; }
318 | }
319 | @keyframes Select-animation-fadeIn {
320 | from { opacity: 0; }
321 | to { opacity: 1; }
322 | }
323 |
--------------------------------------------------------------------------------
/less/default.less:
--------------------------------------------------------------------------------
1 | @import "select.less";
2 |
--------------------------------------------------------------------------------
/less/menu.less:
--------------------------------------------------------------------------------
1 | //
2 | // Select Menu
3 | // ------------------------------
4 |
5 |
6 | // wrapper around the menu
7 |
8 | .Select-menu-outer {
9 | // Unfortunately, having both border-radius and allows scrolling using overflow defined on the same
10 | // element forces the browser to repaint on scroll. However, if these definitions are split into an
11 | // outer and an inner element, the browser is able to optimize the scrolling behavior and does not
12 | // have to repaint on scroll.
13 | .border-bottom-radius( @select-input-border-radius );
14 | background-color: @select-input-bg;
15 | border: 1px solid @select-input-border-color;
16 | border-top-color: mix(@select-input-bg, @select-input-border-color, 50%);
17 | box-shadow: @select-menu-box-shadow;
18 | box-sizing: border-box;
19 | margin-top: -1px;
20 | max-height: @select-menu-max-height;
21 | position: absolute;
22 | top: 100%;
23 | width: 100%;
24 | z-index: @select-menu-zindex;
25 | -webkit-overflow-scrolling: touch;
26 | }
27 |
28 |
29 | // wrapper
30 |
31 | .Select-menu {
32 | max-height: (@select-menu-max-height - 2px);
33 | overflow-y: auto;
34 | }
35 |
36 |
37 | // option groups
38 |
39 | .Select-option-group-label {
40 | box-sizing: border-box;
41 | background-color: @select-option-bg;
42 | color: @select-option-color;
43 | font-weight: bold;
44 | cursor: default;
45 | display: block;
46 | padding: @select-padding-vertical @select-padding-horizontal;
47 | }
48 |
49 | .Select-option-group-label ~ .Select-option,
50 | .Select-option-group-label ~ .Select-option-group {
51 | padding-left: @select-padding-horizontal * 2;
52 | }
53 |
54 |
55 | // options
56 |
57 | .Select-option {
58 | box-sizing: border-box;
59 | background-color: @select-option-bg;
60 | color: @select-option-color;
61 | cursor: pointer;
62 | display: block;
63 | padding: @select-padding-vertical @select-padding-horizontal;
64 |
65 | &:last-child {
66 | .border-bottom-radius( @select-input-border-radius );
67 | }
68 |
69 | &.is-selected {
70 | background-color: @select-option-selected-bg-fb; /* Fallback color for IE 8 */
71 | background-color: @select-option-selected-bg;
72 | color: @select-option-selected-color;
73 | }
74 |
75 | &.is-focused {
76 | background-color: @select-option-focused-bg-fb; /* Fallback color for IE 8 */
77 | background-color: @select-option-focused-bg;
78 | color: @select-option-focused-color;
79 | }
80 |
81 | &.is-disabled {
82 | color: @select-option-disabled-color;
83 | cursor: default;
84 | }
85 |
86 | }
87 |
88 |
89 | // no results
90 |
91 | .Select-noresults {
92 | box-sizing: border-box;
93 | color: @select-noresults-color;
94 | cursor: default;
95 | display: block;
96 | padding: @select-padding-vertical @select-padding-horizontal;
97 | }
98 |
--------------------------------------------------------------------------------
/less/mixins.less:
--------------------------------------------------------------------------------
1 | //
2 | // Mixins
3 | // ------------------------------
4 |
5 |
6 | // Utilities
7 |
8 | .size(@width; @height) {
9 | width: @width;
10 | height: @height;
11 | }
12 | .square(@size) {
13 | .size(@size; @size);
14 | }
15 | .border-top-radius(@radius) {
16 | border-top-right-radius: @radius;
17 | border-top-left-radius: @radius;
18 | }
19 | .border-right-radius(@radius) {
20 | border-bottom-right-radius: @radius;
21 | border-top-right-radius: @radius;
22 | }
23 | .border-bottom-radius(@radius) {
24 | border-bottom-right-radius: @radius;
25 | border-bottom-left-radius: @radius;
26 | }
27 | .border-left-radius(@radius) {
28 | border-bottom-left-radius: @radius;
29 | border-top-left-radius: @radius;
30 | }
31 |
32 |
33 | // Vendor Prefixes
34 |
35 | .animation(@animation) {
36 | -webkit-animation: @animation;
37 | -o-animation: @animation;
38 | animation: @animation;
39 | }
40 | .box-sizing(@boxmodel) {
41 | -webkit-box-sizing: @boxmodel;
42 | -moz-box-sizing: @boxmodel;
43 | box-sizing: @boxmodel;
44 | }
45 | .rotate(@degrees) {
46 | -webkit-transform: rotate(@degrees);
47 | -ms-transform: rotate(@degrees); // IE9 only
48 | -o-transform: rotate(@degrees);
49 | transform: rotate(@degrees);
50 | }
51 | .transform(@transform) {
52 | -webkit-transform: @transform;
53 | -moz-transform: @transform;
54 | -ms-transform: @transform;
55 | transform: @transform;
56 | }
57 |
--------------------------------------------------------------------------------
/less/multi.less:
--------------------------------------------------------------------------------
1 | //
2 | // Multi-Select
3 | // ------------------------------
4 |
5 |
6 | // Base
7 |
8 | .Select--multi {
9 |
10 | // add margin to the input element
11 | .Select-input {
12 | vertical-align: middle;
13 | // border: 1px solid transparent;
14 | margin-left: @select-padding-horizontal;
15 | padding: 0;
16 | }
17 | &.Select--rtl .Select-input {
18 | margin-left: 0;
19 | margin-right: @select-padding-horizontal;
20 | }
21 |
22 | // reduce margin once there is value
23 | &.has-value .Select-input {
24 | margin-left: @select-item-gutter;
25 | }
26 |
27 | // Items
28 | .Select-value {
29 | background-color: @select-item-bg-fb; /* Fallback color for IE 8 */
30 | background-color: @select-item-bg;
31 | border-radius: @select-item-border-radius;
32 | border: 1px solid @select-item-border-color-fb; /* Fallback color for IE 8 */
33 | border: 1px solid @select-item-border-color;
34 | color: @select-item-color;
35 | display: inline-block;
36 | font-size: @select-item-font-size;
37 | line-height: 1.4;
38 | margin-left: @select-item-gutter;
39 | margin-top: @select-item-gutter;
40 | vertical-align: top;
41 | }
42 |
43 | // common
44 | .Select-value-icon,
45 | .Select-value-label {
46 | display: inline-block;
47 | vertical-align: middle;
48 | }
49 |
50 | // label
51 | .Select-value-label {
52 | .border-right-radius( @select-item-border-radius );
53 | cursor: default;
54 | padding: @select-item-padding-vertical @select-item-padding-horizontal;
55 | }
56 | a.Select-value-label {
57 | color: @select-item-color;
58 | cursor: pointer;
59 | text-decoration: none;
60 |
61 | &:hover {
62 | text-decoration: underline;
63 | }
64 | }
65 |
66 | // icon
67 | .Select-value-icon {
68 | cursor: pointer;
69 | .border-left-radius( @select-item-border-radius );
70 | border-right: 1px solid @select-item-border-color-fb; /* Fallback color for IE 8 */
71 | border-right: 1px solid @select-item-border-color;
72 |
73 | // move the baseline up by 1px
74 | padding: (@select-item-padding-vertical - 1) @select-item-padding-horizontal (@select-item-padding-vertical + 1);
75 |
76 | &:hover,
77 | &:focus {
78 | background-color: @select-item-hover-bg-fb; /* Fallback color for IE 8 */
79 | background-color: @select-item-hover-bg;
80 | color: @select-item-hover-color;
81 | }
82 | &:active {
83 | background-color: @select-item-border-color-fb; /* Fallback color for IE 8 */
84 | background-color: @select-item-border-color;
85 | }
86 | }
87 | &.Select--rtl {
88 | .Select-value {
89 | margin-left: 0;
90 | margin-right: @select-item-gutter;
91 | }
92 | .Select-value-icon {
93 | border-right: none;
94 | border-left: 1px solid @select-item-border-color-fb; /* Fallback color for IE 8 */
95 | border-left: 1px solid @select-item-border-color;
96 | }
97 | }
98 | }
99 |
100 | .Select--multi.is-disabled {
101 | .Select-value {
102 | background-color: @select-item-disabled-bg;
103 | border: 1px solid @select-item-disabled-border-color;
104 | color: @select-item-disabled-color;
105 | }
106 | // icon
107 | .Select-value-icon {
108 | cursor: not-allowed;
109 | border-right: 1px solid @select-item-disabled-border-color;
110 |
111 | &:hover,
112 | &:focus,
113 | &:active {
114 | background-color: @select-item-disabled-bg;
115 | }
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/less/select.less:
--------------------------------------------------------------------------------
1 | /**
2 | * React Select Plus
3 | * =================
4 | * MIT License: https://github.com/HubSpot/react-select-plus
5 | */
6 |
7 | // Variables
8 | // ------------------------------
9 |
10 | // common
11 | @select-primary-color: #007eff;
12 |
13 | // control options
14 | @select-input-bg: #fff;
15 | @select-input-bg-disabled: #f9f9f9;
16 | @select-input-bg-focus: @select-input-bg;
17 | @select-input-border-color: #ccc;
18 | @select-input-border-radius: 4px;
19 | @select-input-border-focus: @select-primary-color;
20 | @select-input-box-shadow-focus: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 0 5px -1px fade(@select-input-border-focus, 50%);
21 | @select-input-border-width: 1px;
22 | @select-input-height: 36px;
23 | @select-input-internal-height: (@select-input-height - (@select-input-border-width * 2));
24 | @select-input-placeholder: #aaa;
25 | @select-text-color: #333;
26 | @select-link-hover-color: @select-input-border-focus;
27 | @select-input-hover-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.06);
28 |
29 | @select-padding-vertical: 8px;
30 | @select-padding-horizontal: 10px;
31 |
32 | // menu options
33 | @select-menu-zindex: 1;
34 | @select-menu-max-height: 200px;
35 | @select-menu-box-shadow: @select-input-hover-box-shadow;
36 |
37 | @select-option-color: lighten(@select-text-color, 20%);
38 | @select-option-bg: @select-input-bg;
39 | @select-option-focused-color: @select-text-color;
40 | @select-option-focused-bg: fade(@select-primary-color, 8%);
41 | @select-option-focused-bg-fb: mix(@select-primary-color, @select-option-bg, 8%); // Fallback color for IE 8
42 | @select-option-selected-color: @select-text-color;
43 | @select-option-selected-bg: fade(@select-primary-color, 4%);
44 | @select-option-selected-bg-fb: mix(@select-primary-color, @select-option-bg, 4%); // Fallback color for IE 8
45 | @select-option-disabled-color: lighten(@select-text-color, 60%);
46 |
47 | @select-noresults-color: lighten(@select-text-color, 40%);
48 |
49 | // clear "x" button
50 | @select-clear-size: floor((@select-input-height / 2));
51 | @select-clear-color: #999;
52 | @select-clear-hover-color: #D0021B; // red
53 | @select-clear-width: (@select-input-internal-height / 2);
54 |
55 | // arrow indicator
56 | @select-arrow-color: #999;
57 | @select-arrow-color-hover: #666;
58 | @select-arrow-width: 5px;
59 |
60 | // loading indicator
61 | @select-loading-size: 16px;
62 | @select-loading-color: @select-text-color;
63 | @select-loading-color-bg: @select-input-border-color;
64 |
65 | @select-right-padding: 5px;
66 |
67 | // multi-select item
68 | @select-item-font-size: .9em;
69 |
70 | @select-item-bg: fade(@select-primary-color, 8%);
71 | @select-item-bg-fb: mix(@select-primary-color, @select-input-bg, 8%); // Fallback color for IE 8
72 | @select-item-color: @select-primary-color;
73 | @select-item-border-color: fade(@select-primary-color, 24%);
74 | @select-item-border-color-fb: mix(@select-primary-color, @select-input-bg, 24%); // Fallback color for IE 8
75 | @select-item-hover-color: darken(@select-item-color, 5%);
76 | @select-item-hover-bg: darken(@select-item-bg, 5%);
77 | @select-item-hover-bg-fb: mix(darken(@select-primary-color, 5%), @select-item-bg-fb, 8%); // Fallback color for IE 8
78 | @select-item-disabled-color: #333;
79 | @select-item-disabled-bg: #fcfcfc;
80 | @select-item-disabled-border-color: darken(@select-item-disabled-bg, 10%);
81 |
82 | @select-item-border-radius: 2px;
83 | @select-item-gutter: 5px;
84 |
85 | @select-item-padding-horizontal: 5px;
86 | @select-item-padding-vertical: 2px;
87 |
88 | // imports
89 | @import "control.less";
90 | @import "menu.less";
91 | @import "mixins.less";
92 | @import "multi.less";
93 | @import "spinner.less";
94 |
--------------------------------------------------------------------------------
/less/spinner.less:
--------------------------------------------------------------------------------
1 | //
2 | // Spinner
3 | // ------------------------------
4 |
5 | .Select-spinner(@size, @orbit, @satellite) {
6 | .animation( Select-animation-spin 400ms infinite linear );
7 | .square(@size);
8 | box-sizing: border-box;
9 | border-radius: 50%;
10 | border: floor((@size / 8)) solid @orbit;
11 | border-right-color: @satellite;
12 | display: inline-block;
13 | position: relative;
14 |
15 | }
16 |
17 | @keyframes Select-animation-spin {
18 | to { transform: rotate(1turn); }
19 | }
20 | @-webkit-keyframes Select-animation-spin {
21 | to { -webkit-transform: rotate(1turn); }
22 | }
23 |
--------------------------------------------------------------------------------
/lib/AsyncCreatable.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 |
7 | var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
8 |
9 | var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
10 |
11 | var _propTypes = require('prop-types');
12 |
13 | var _propTypes2 = _interopRequireDefault(_propTypes);
14 |
15 | var _react = require('react');
16 |
17 | var _react2 = _interopRequireDefault(_react);
18 |
19 | var _Async = require('./Async');
20 |
21 | var _Async2 = _interopRequireDefault(_Async);
22 |
23 | var _Creatable = require('./Creatable');
24 |
25 | var _Creatable2 = _interopRequireDefault(_Creatable);
26 |
27 | var _Select = require('./Select');
28 |
29 | var _Select2 = _interopRequireDefault(_Select);
30 |
31 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
32 |
33 | function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
34 |
35 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
36 |
37 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
38 |
39 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
40 |
41 | var AsyncCreatableSelect = function (_React$Component) {
42 | _inherits(AsyncCreatableSelect, _React$Component);
43 |
44 | function AsyncCreatableSelect() {
45 | _classCallCheck(this, AsyncCreatableSelect);
46 |
47 | return _possibleConstructorReturn(this, (AsyncCreatableSelect.__proto__ || Object.getPrototypeOf(AsyncCreatableSelect)).apply(this, arguments));
48 | }
49 |
50 | _createClass(AsyncCreatableSelect, [{
51 | key: 'focus',
52 | value: function focus() {
53 | this.select.focus();
54 | }
55 | }, {
56 | key: 'render',
57 | value: function render() {
58 | var _this2 = this;
59 |
60 | return _react2.default.createElement(
61 | _Async2.default,
62 | this.props,
63 | function (_ref) {
64 | var ref = _ref.ref,
65 | asyncProps = _objectWithoutProperties(_ref, ['ref']);
66 |
67 | var asyncRef = ref;
68 | return _react2.default.createElement(
69 | _Creatable2.default,
70 | asyncProps,
71 | function (_ref2) {
72 | var ref = _ref2.ref,
73 | creatableProps = _objectWithoutProperties(_ref2, ['ref']);
74 |
75 | var creatableRef = ref;
76 | return _this2.props.children(_extends({}, creatableProps, {
77 | ref: function ref(select) {
78 | creatableRef(select);
79 | asyncRef(select);
80 | _this2.select = select;
81 | }
82 | }));
83 | }
84 | );
85 | }
86 | );
87 | }
88 | }]);
89 |
90 | return AsyncCreatableSelect;
91 | }(_react2.default.Component);
92 |
93 | var defaultChildren = function defaultChildren(props) {
94 | return _react2.default.createElement(_Select2.default, props);
95 | };
96 |
97 | AsyncCreatableSelect.propTypes = {
98 | children: _propTypes2.default.func.isRequired // Child function responsible for creating the inner Select component; (props: Object): PropTypes.element
99 | };
100 |
101 | AsyncCreatableSelect.defaultProps = {
102 | children: defaultChildren
103 | };
104 |
105 | exports.default = AsyncCreatableSelect;
--------------------------------------------------------------------------------
/lib/Dropdown.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 |
7 | var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
8 |
9 | var _react = require('react');
10 |
11 | var _react2 = _interopRequireDefault(_react);
12 |
13 | var _propTypes = require('prop-types');
14 |
15 | var _propTypes2 = _interopRequireDefault(_propTypes);
16 |
17 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
18 |
19 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
20 |
21 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
22 |
23 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
24 |
25 | var Dropdown = function (_React$Component) {
26 | _inherits(Dropdown, _React$Component);
27 |
28 | function Dropdown() {
29 | _classCallCheck(this, Dropdown);
30 |
31 | return _possibleConstructorReturn(this, (Dropdown.__proto__ || Object.getPrototypeOf(Dropdown)).apply(this, arguments));
32 | }
33 |
34 | _createClass(Dropdown, [{
35 | key: 'render',
36 | value: function render() {
37 | // This component adds no markup
38 | return this.props.children;
39 | }
40 | }]);
41 |
42 | return Dropdown;
43 | }(_react2.default.Component);
44 |
45 | ;
46 |
47 | Dropdown.propTypes = {
48 | children: _propTypes2.default.node
49 | };
50 |
51 | exports.default = Dropdown;
--------------------------------------------------------------------------------
/lib/Option.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 |
7 | var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
8 |
9 | var _classnames = require('classnames');
10 |
11 | var _classnames2 = _interopRequireDefault(_classnames);
12 |
13 | var _propTypes = require('prop-types');
14 |
15 | var _propTypes2 = _interopRequireDefault(_propTypes);
16 |
17 | var _react = require('react');
18 |
19 | var _react2 = _interopRequireDefault(_react);
20 |
21 | var _blockEvent = require('./utils/blockEvent');
22 |
23 | var _blockEvent2 = _interopRequireDefault(_blockEvent);
24 |
25 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
26 |
27 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
28 |
29 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
30 |
31 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
32 |
33 | var Option = function (_React$Component) {
34 | _inherits(Option, _React$Component);
35 |
36 | function Option(props) {
37 | _classCallCheck(this, Option);
38 |
39 | var _this = _possibleConstructorReturn(this, (Option.__proto__ || Object.getPrototypeOf(Option)).call(this, props));
40 |
41 | _this.handleMouseDown = _this.handleMouseDown.bind(_this);
42 | _this.handleMouseEnter = _this.handleMouseEnter.bind(_this);
43 | _this.handleMouseMove = _this.handleMouseMove.bind(_this);
44 | _this.handleTouchStart = _this.handleTouchStart.bind(_this);
45 | _this.handleTouchEnd = _this.handleTouchEnd.bind(_this);
46 | _this.handleTouchMove = _this.handleTouchMove.bind(_this);
47 | _this.onFocus = _this.onFocus.bind(_this);
48 | return _this;
49 | }
50 |
51 | _createClass(Option, [{
52 | key: 'handleMouseDown',
53 | value: function handleMouseDown(event) {
54 | event.preventDefault();
55 | event.stopPropagation();
56 | this.props.onSelect(this.props.option, event);
57 | }
58 | }, {
59 | key: 'handleMouseEnter',
60 | value: function handleMouseEnter(event) {
61 | this.onFocus(event);
62 | }
63 | }, {
64 | key: 'handleMouseMove',
65 | value: function handleMouseMove(event) {
66 | this.onFocus(event);
67 | }
68 | }, {
69 | key: 'handleTouchEnd',
70 | value: function handleTouchEnd(event) {
71 | // Check if the view is being dragged, In this case
72 | // we don't want to fire the click event (because the user only wants to scroll)
73 | if (this.dragging) return;
74 |
75 | this.handleMouseDown(event);
76 | }
77 | }, {
78 | key: 'handleTouchMove',
79 | value: function handleTouchMove() {
80 | // Set a flag that the view is being dragged
81 | this.dragging = true;
82 | }
83 | }, {
84 | key: 'handleTouchStart',
85 | value: function handleTouchStart() {
86 | // Set a flag that the view is not being dragged
87 | this.dragging = false;
88 | }
89 | }, {
90 | key: 'onFocus',
91 | value: function onFocus(event) {
92 | if (!this.props.isFocused) {
93 | this.props.onFocus(this.props.option, event);
94 | }
95 | }
96 | }, {
97 | key: 'render',
98 | value: function render() {
99 | var _props = this.props,
100 | option = _props.option,
101 | instancePrefix = _props.instancePrefix,
102 | optionIndex = _props.optionIndex;
103 |
104 | var className = (0, _classnames2.default)(this.props.className, option.className);
105 |
106 | return option.disabled ? _react2.default.createElement(
107 | 'div',
108 | { className: className,
109 | onMouseDown: _blockEvent2.default,
110 | onClick: _blockEvent2.default },
111 | this.props.children
112 | ) : _react2.default.createElement(
113 | 'div',
114 | { className: className,
115 | style: option.style,
116 | role: 'option',
117 | 'aria-label': option.label,
118 | onMouseDown: this.handleMouseDown,
119 | onMouseEnter: this.handleMouseEnter,
120 | onMouseMove: this.handleMouseMove,
121 | onTouchStart: this.handleTouchStart,
122 | onTouchMove: this.handleTouchMove,
123 | onTouchEnd: this.handleTouchEnd,
124 | id: instancePrefix + '-option-' + optionIndex,
125 | title: option.title },
126 | this.props.children
127 | );
128 | }
129 | }]);
130 |
131 | return Option;
132 | }(_react2.default.Component);
133 |
134 | Option.propTypes = {
135 | children: _propTypes2.default.node,
136 | className: _propTypes2.default.string, // className (based on mouse position)
137 | instancePrefix: _propTypes2.default.string.isRequired, // unique prefix for the ids (used for aria)
138 | isDisabled: _propTypes2.default.bool, // the option is disabled
139 | isFocused: _propTypes2.default.bool, // the option is focused
140 | isSelected: _propTypes2.default.bool, // the option is selected
141 | onFocus: _propTypes2.default.func, // method to handle mouseEnter on option element
142 | onSelect: _propTypes2.default.func, // method to handle click on option element
143 | onUnfocus: _propTypes2.default.func, // method to handle mouseLeave on option element
144 | option: _propTypes2.default.object.isRequired, // object that is base for that option
145 | optionIndex: _propTypes2.default.number // index of the option, used to generate unique ids for aria
146 | };
147 |
148 | exports.default = Option;
--------------------------------------------------------------------------------
/lib/OptionGroup.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 |
7 | var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
8 |
9 | var _react = require('react');
10 |
11 | var _react2 = _interopRequireDefault(_react);
12 |
13 | var _propTypes = require('prop-types');
14 |
15 | var _propTypes2 = _interopRequireDefault(_propTypes);
16 |
17 | var _classnames = require('classnames');
18 |
19 | var _classnames2 = _interopRequireDefault(_classnames);
20 |
21 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
22 |
23 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
24 |
25 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
26 |
27 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
28 |
29 | var OptionGroup = function (_React$Component) {
30 | _inherits(OptionGroup, _React$Component);
31 |
32 | function OptionGroup(props) {
33 | _classCallCheck(this, OptionGroup);
34 |
35 | var _this = _possibleConstructorReturn(this, (OptionGroup.__proto__ || Object.getPrototypeOf(OptionGroup)).call(this, props));
36 |
37 | _this.handleMouseDown = _this.handleMouseDown.bind(_this);
38 | _this.handleTouchEnd = _this.handleTouchEnd.bind(_this);
39 | _this.handleTouchMove = _this.handleTouchMove.bind(_this);
40 | _this.handleTouchStart = _this.handleTouchStart.bind(_this);
41 | return _this;
42 | }
43 |
44 | _createClass(OptionGroup, [{
45 | key: 'blockEvent',
46 | value: function blockEvent(event) {
47 | event.preventDefault();
48 | event.stopPropagation();
49 | if (event.target.tagName !== 'A' || !('href' in event.target)) {
50 | return;
51 | }
52 | if (event.target.target) {
53 | window.open(event.target.href, event.target.target);
54 | } else {
55 | window.location.href = event.target.href;
56 | }
57 | }
58 | }, {
59 | key: 'handleMouseDown',
60 | value: function handleMouseDown(event) {
61 | event.preventDefault();
62 | event.stopPropagation();
63 | }
64 | }, {
65 | key: 'handleTouchEnd',
66 | value: function handleTouchEnd(event) {
67 | // Check if the view is being dragged, In this case
68 | // we don't want to fire the click event (because the user only wants to scroll)
69 | if (this.dragging) return;
70 |
71 | this.handleMouseDown(event);
72 | }
73 | }, {
74 | key: 'handleTouchMove',
75 | value: function handleTouchMove(event) {
76 | // Set a flag that the view is being dragged
77 | this.dragging = true;
78 | }
79 | }, {
80 | key: 'handleTouchStart',
81 | value: function handleTouchStart(event) {
82 | // Set a flag that the view is not being dragged
83 | this.dragging = false;
84 | }
85 | }, {
86 | key: 'render',
87 | value: function render() {
88 | var option = this.props.option;
89 |
90 | var className = (0, _classnames2.default)(this.props.className, option.className);
91 |
92 | return option.disabled ? _react2.default.createElement(
93 | 'div',
94 | { className: className,
95 | onMouseDown: this.blockEvent,
96 | onClick: this.blockEvent },
97 | this.props.children
98 | ) : _react2.default.createElement(
99 | 'div',
100 | { className: className,
101 | style: option.style,
102 | onMouseDown: this.handleMouseDown,
103 | onMouseEnter: this.handleMouseEnter,
104 | onMouseMove: this.handleMouseMove,
105 | onTouchStart: this.handleTouchStart,
106 | onTouchMove: this.handleTouchMove,
107 | onTouchEnd: this.handleTouchEnd,
108 | title: option.title },
109 | _react2.default.createElement(
110 | 'div',
111 | { className: 'Select-option-group-label' },
112 | this.props.label
113 | ),
114 | this.props.children
115 | );
116 | }
117 | }]);
118 |
119 | return OptionGroup;
120 | }(_react2.default.Component);
121 |
122 | ;
123 |
124 | OptionGroup.propTypes = {
125 | children: _propTypes2.default.any,
126 | className: _propTypes2.default.string, // className (based on mouse position)
127 | label: _propTypes2.default.node, // the heading to show above the child options
128 | option: _propTypes2.default.object.isRequired // object that is base for that option group
129 | };
130 |
131 | exports.default = OptionGroup;
--------------------------------------------------------------------------------
/lib/Value.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 |
7 | var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
8 |
9 | var _classnames = require('classnames');
10 |
11 | var _classnames2 = _interopRequireDefault(_classnames);
12 |
13 | var _propTypes = require('prop-types');
14 |
15 | var _propTypes2 = _interopRequireDefault(_propTypes);
16 |
17 | var _react = require('react');
18 |
19 | var _react2 = _interopRequireDefault(_react);
20 |
21 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
22 |
23 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
24 |
25 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
26 |
27 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
28 |
29 | var Value = function (_React$Component) {
30 | _inherits(Value, _React$Component);
31 |
32 | function Value(props) {
33 | _classCallCheck(this, Value);
34 |
35 | var _this = _possibleConstructorReturn(this, (Value.__proto__ || Object.getPrototypeOf(Value)).call(this, props));
36 |
37 | _this.handleMouseDown = _this.handleMouseDown.bind(_this);
38 | _this.onRemove = _this.onRemove.bind(_this);
39 | _this.handleTouchEndRemove = _this.handleTouchEndRemove.bind(_this);
40 | _this.handleTouchMove = _this.handleTouchMove.bind(_this);
41 | _this.handleTouchStart = _this.handleTouchStart.bind(_this);
42 | return _this;
43 | }
44 |
45 | _createClass(Value, [{
46 | key: 'handleMouseDown',
47 | value: function handleMouseDown(event) {
48 | if (event.type === 'mousedown' && event.button !== 0) {
49 | return;
50 | }
51 | if (this.props.onClick) {
52 | event.stopPropagation();
53 | this.props.onClick(this.props.value, event);
54 | return;
55 | }
56 | if (this.props.value.href) {
57 | event.stopPropagation();
58 | }
59 | }
60 | }, {
61 | key: 'onRemove',
62 | value: function onRemove(event) {
63 | event.preventDefault();
64 | event.stopPropagation();
65 | this.props.onRemove(this.props.value);
66 | }
67 | }, {
68 | key: 'handleTouchEndRemove',
69 | value: function handleTouchEndRemove(event) {
70 | // Check if the view is being dragged, In this case
71 | // we don't want to fire the click event (because the user only wants to scroll)
72 | if (this.dragging) return;
73 |
74 | // Fire the mouse events
75 | this.onRemove(event);
76 | }
77 | }, {
78 | key: 'handleTouchMove',
79 | value: function handleTouchMove() {
80 | // Set a flag that the view is being dragged
81 | this.dragging = true;
82 | }
83 | }, {
84 | key: 'handleTouchStart',
85 | value: function handleTouchStart() {
86 | // Set a flag that the view is not being dragged
87 | this.dragging = false;
88 | }
89 | }, {
90 | key: 'renderRemoveIcon',
91 | value: function renderRemoveIcon() {
92 | if (this.props.disabled || !this.props.onRemove) return;
93 | return _react2.default.createElement(
94 | 'span',
95 | { className: 'Select-value-icon',
96 | 'aria-hidden': 'true',
97 | onMouseDown: this.onRemove,
98 | onTouchEnd: this.handleTouchEndRemove,
99 | onTouchStart: this.handleTouchStart,
100 | onTouchMove: this.handleTouchMove },
101 | '\xD7'
102 | );
103 | }
104 | }, {
105 | key: 'renderLabel',
106 | value: function renderLabel() {
107 | var className = 'Select-value-label';
108 | return this.props.onClick || this.props.value.href ? _react2.default.createElement(
109 | 'a',
110 | { className: className, href: this.props.value.href, target: this.props.value.target, onMouseDown: this.handleMouseDown, onTouchEnd: this.handleMouseDown },
111 | this.props.children
112 | ) : _react2.default.createElement(
113 | 'span',
114 | { className: className, role: 'option', 'aria-selected': 'true', id: this.props.id },
115 | this.props.children
116 | );
117 | }
118 | }, {
119 | key: 'render',
120 | value: function render() {
121 | return _react2.default.createElement(
122 | 'div',
123 | { className: (0, _classnames2.default)('Select-value', this.props.value.className),
124 | style: this.props.value.style,
125 | title: this.props.value.title
126 | },
127 | this.renderRemoveIcon(),
128 | this.renderLabel()
129 | );
130 | }
131 | }]);
132 |
133 | return Value;
134 | }(_react2.default.Component);
135 |
136 | Value.propTypes = {
137 | children: _propTypes2.default.node,
138 | disabled: _propTypes2.default.bool, // disabled prop passed to ReactSelect
139 | id: _propTypes2.default.string, // Unique id for the value - used for aria
140 | onClick: _propTypes2.default.func, // method to handle click on value label
141 | onRemove: _propTypes2.default.func, // method to handle removal of the value
142 | value: _propTypes2.default.object.isRequired // the option object for this value
143 | };
144 |
145 | exports.default = Value;
--------------------------------------------------------------------------------
/lib/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.defaultFilterOptions = exports.defaultClearRenderer = exports.defaultArrowRenderer = exports.defaultMenuRenderer = exports.Option = exports.Value = exports.Creatable = exports.AsyncCreatable = exports.Async = undefined;
7 |
8 | var _Select = require('./Select');
9 |
10 | var _Select2 = _interopRequireDefault(_Select);
11 |
12 | var _Async = require('./Async');
13 |
14 | var _Async2 = _interopRequireDefault(_Async);
15 |
16 | var _AsyncCreatable = require('./AsyncCreatable');
17 |
18 | var _AsyncCreatable2 = _interopRequireDefault(_AsyncCreatable);
19 |
20 | var _Creatable = require('./Creatable');
21 |
22 | var _Creatable2 = _interopRequireDefault(_Creatable);
23 |
24 | var _Value = require('./Value');
25 |
26 | var _Value2 = _interopRequireDefault(_Value);
27 |
28 | var _Option = require('./Option');
29 |
30 | var _Option2 = _interopRequireDefault(_Option);
31 |
32 | var _defaultMenuRenderer = require('./utils/defaultMenuRenderer');
33 |
34 | var _defaultMenuRenderer2 = _interopRequireDefault(_defaultMenuRenderer);
35 |
36 | var _defaultArrowRenderer = require('./utils/defaultArrowRenderer');
37 |
38 | var _defaultArrowRenderer2 = _interopRequireDefault(_defaultArrowRenderer);
39 |
40 | var _defaultClearRenderer = require('./utils/defaultClearRenderer');
41 |
42 | var _defaultClearRenderer2 = _interopRequireDefault(_defaultClearRenderer);
43 |
44 | var _defaultFilterOptions = require('./utils/defaultFilterOptions');
45 |
46 | var _defaultFilterOptions2 = _interopRequireDefault(_defaultFilterOptions);
47 |
48 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
49 |
50 | _Select2.default.Async = _Async2.default;
51 | _Select2.default.AsyncCreatable = _AsyncCreatable2.default;
52 | _Select2.default.Creatable = _Creatable2.default;
53 | _Select2.default.Value = _Value2.default;
54 | _Select2.default.Option = _Option2.default;
55 |
56 | exports.default = _Select2.default;
57 | exports.Async = _Async2.default;
58 | exports.AsyncCreatable = _AsyncCreatable2.default;
59 | exports.Creatable = _Creatable2.default;
60 | exports.Value = _Value2.default;
61 | exports.Option = _Option2.default;
62 | exports.defaultMenuRenderer = _defaultMenuRenderer2.default;
63 | exports.defaultArrowRenderer = _defaultArrowRenderer2.default;
64 | exports.defaultClearRenderer = _defaultClearRenderer2.default;
65 | exports.defaultFilterOptions = _defaultFilterOptions2.default;
--------------------------------------------------------------------------------
/lib/utils/blockEvent.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 |
7 | exports.default = function (event) {
8 | event.preventDefault();
9 | event.stopPropagation();
10 | if (event.target.tagName !== 'A' || !('href' in event.target)) {
11 | return;
12 | }
13 | if (event.target.target) {
14 | window.open(event.target.href, event.target.target);
15 | } else {
16 | window.location.href = event.target.href;
17 | }
18 | };
--------------------------------------------------------------------------------
/lib/utils/defaultArrowRenderer.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 |
7 | var _react = require('react');
8 |
9 | var _react2 = _interopRequireDefault(_react);
10 |
11 | var _propTypes = require('prop-types');
12 |
13 | var _propTypes2 = _interopRequireDefault(_propTypes);
14 |
15 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
16 |
17 | var arrowRenderer = function arrowRenderer(_ref) {
18 | var onMouseDown = _ref.onMouseDown;
19 |
20 | return _react2.default.createElement('span', {
21 | className: 'Select-arrow',
22 | onMouseDown: onMouseDown
23 | });
24 | };
25 |
26 | arrowRenderer.propTypes = {
27 | onMouseDown: _propTypes2.default.func
28 | };
29 |
30 | exports.default = arrowRenderer;
--------------------------------------------------------------------------------
/lib/utils/defaultClearRenderer.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 |
7 | var _react = require('react');
8 |
9 | var _react2 = _interopRequireDefault(_react);
10 |
11 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
12 |
13 | var clearRenderer = function clearRenderer() {
14 | return _react2.default.createElement('span', {
15 | className: 'Select-clear',
16 | dangerouslySetInnerHTML: { __html: '×' }
17 | });
18 | };
19 |
20 | exports.default = clearRenderer;
--------------------------------------------------------------------------------
/lib/utils/defaultFilterOptions.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 |
7 | var _stripDiacritics = require('./stripDiacritics');
8 |
9 | var _stripDiacritics2 = _interopRequireDefault(_stripDiacritics);
10 |
11 | var _trim = require('./trim');
12 |
13 | var _trim2 = _interopRequireDefault(_trim);
14 |
15 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
16 |
17 | var isValid = function isValid(value) {
18 | return typeof value !== 'undefined' && value !== null && value !== '';
19 | };
20 |
21 | var filterOptions = function filterOptions(options, filterValue, excludeOptions, props) {
22 | if (props.ignoreAccents) {
23 | filterValue = (0, _stripDiacritics2.default)(filterValue);
24 | }
25 |
26 | if (props.ignoreCase) {
27 | filterValue = filterValue.toLowerCase();
28 | }
29 |
30 | if (props.trimFilter) {
31 | filterValue = (0, _trim2.default)(filterValue);
32 | }
33 |
34 | if (excludeOptions) excludeOptions = excludeOptions.map(function (i) {
35 | return i[props.valueKey];
36 | });
37 |
38 | return options.filter(function (option) {
39 | if (excludeOptions && excludeOptions.indexOf(option[props.valueKey]) > -1) return false;
40 | if (props.filterOption) return props.filterOption.call(undefined, option, filterValue);
41 | if (!filterValue) return true;
42 |
43 | var value = option[props.valueKey];
44 | var label = option[props.labelKey];
45 | var hasValue = isValid(value);
46 | var hasLabel = isValid(label);
47 |
48 | if (!hasValue && !hasLabel) {
49 | return false;
50 | }
51 |
52 | var valueTest = hasValue ? String(value) : null;
53 | var labelTest = hasLabel ? String(label) : null;
54 |
55 | if (props.ignoreAccents) {
56 | if (valueTest && props.matchProp !== 'label') valueTest = (0, _stripDiacritics2.default)(valueTest);
57 | if (labelTest && props.matchProp !== 'value') labelTest = (0, _stripDiacritics2.default)(labelTest);
58 | }
59 |
60 | if (props.ignoreCase) {
61 | if (valueTest && props.matchProp !== 'label') valueTest = valueTest.toLowerCase();
62 | if (labelTest && props.matchProp !== 'value') labelTest = labelTest.toLowerCase();
63 | }
64 |
65 | return props.matchPos === 'start' ? valueTest && props.matchProp !== 'label' && valueTest.substr(0, filterValue.length) === filterValue || labelTest && props.matchProp !== 'value' && labelTest.substr(0, filterValue.length) === filterValue : valueTest && props.matchProp !== 'label' && valueTest.indexOf(filterValue) >= 0 || labelTest && props.matchProp !== 'value' && labelTest.indexOf(filterValue) >= 0;
66 | });
67 | };
68 |
69 | exports.default = filterOptions;
--------------------------------------------------------------------------------
/lib/utils/defaultMenuRenderer.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 |
7 | var _classnames = require('classnames');
8 |
9 | var _classnames2 = _interopRequireDefault(_classnames);
10 |
11 | var _propTypes = require('prop-types');
12 |
13 | var _propTypes2 = _interopRequireDefault(_propTypes);
14 |
15 | var _react = require('react');
16 |
17 | var _react2 = _interopRequireDefault(_react);
18 |
19 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
20 |
21 | var isGroup = function isGroup(option) {
22 | return option && Array.isArray(option.options);
23 | };
24 |
25 | var menuRenderer = function menuRenderer(_ref) {
26 | var focusedOption = _ref.focusedOption,
27 | focusOption = _ref.focusOption,
28 | inputValue = _ref.inputValue,
29 | instancePrefix = _ref.instancePrefix,
30 | onFocus = _ref.onFocus,
31 | onOptionRef = _ref.onOptionRef,
32 | onSelect = _ref.onSelect,
33 | optionClassName = _ref.optionClassName,
34 | optionComponent = _ref.optionComponent,
35 | optionGroupComponent = _ref.optionGroupComponent,
36 | optionRenderer = _ref.optionRenderer,
37 | options = _ref.options,
38 | removeValue = _ref.removeValue,
39 | selectValue = _ref.selectValue,
40 | valueArray = _ref.valueArray,
41 | valueKey = _ref.valueKey;
42 |
43 | var OptionGroup = optionGroupComponent;
44 | var Option = optionComponent;
45 | var renderLabel = optionRenderer;
46 |
47 | var renderOptions = function renderOptions(optionsSubset) {
48 | return optionsSubset.map(function (option, i) {
49 | if (isGroup(option)) {
50 | var optionGroupClass = (0, _classnames2.default)({
51 | 'Select-option-group': true
52 | });
53 |
54 | return _react2.default.createElement(
55 | OptionGroup,
56 | {
57 | className: optionGroupClass,
58 | key: 'option-group-' + i,
59 | label: renderLabel(option),
60 | option: option,
61 | optionIndex: i
62 | },
63 | renderOptions(option.options)
64 | );
65 | } else {
66 | var isSelected = valueArray && valueArray.indexOf(option) > -1;
67 | var isFocused = option === focusedOption;
68 | var optionRef = isFocused ? 'focused' : null;
69 | var optionClass = (0, _classnames2.default)(optionClassName, {
70 | 'Select-option': true,
71 | 'is-selected': isSelected,
72 | 'is-focused': isFocused,
73 | 'is-disabled': option.disabled
74 | });
75 |
76 | return _react2.default.createElement(
77 | Option,
78 | {
79 | className: optionClass,
80 | focusOption: focusOption,
81 | inputValue: inputValue,
82 | instancePrefix: instancePrefix,
83 | isDisabled: option.disabled,
84 | isFocused: isFocused,
85 | isSelected: isSelected,
86 | key: 'option-' + i + '-' + option[valueKey],
87 | onFocus: onFocus,
88 | onSelect: onSelect,
89 | option: option,
90 | optionIndex: i,
91 | ref: function ref(_ref2) {
92 | onOptionRef(_ref2, isFocused);
93 | },
94 | removeValue: removeValue,
95 | selectValue: selectValue
96 | },
97 | renderLabel(option, i)
98 | );
99 | }
100 | });
101 | };
102 |
103 | return renderOptions(options);
104 | };
105 |
106 | menuRenderer.propTypes = {
107 | focusOption: _propTypes2.default.func,
108 | focusedOption: _propTypes2.default.object,
109 | inputValue: _propTypes2.default.string,
110 | instancePrefix: _propTypes2.default.string,
111 | onFocus: _propTypes2.default.func,
112 | onOptionRef: _propTypes2.default.func,
113 | onSelect: _propTypes2.default.func,
114 | optionClassName: _propTypes2.default.string,
115 | optionComponent: _propTypes2.default.func,
116 | optionRenderer: _propTypes2.default.func,
117 | options: _propTypes2.default.array,
118 | removeValue: _propTypes2.default.func,
119 | selectValue: _propTypes2.default.func,
120 | valueArray: _propTypes2.default.array,
121 | valueKey: _propTypes2.default.string
122 | };
123 |
124 | exports.default = menuRenderer;
--------------------------------------------------------------------------------
/lib/utils/stripDiacritics.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | var map = [{ 'base': 'A', 'letters': /[\u0041\u24B6\uFF21\u00C0\u00C1\u00C2\u1EA6\u1EA4\u1EAA\u1EA8\u00C3\u0100\u0102\u1EB0\u1EAE\u1EB4\u1EB2\u0226\u01E0\u00C4\u01DE\u1EA2\u00C5\u01FA\u01CD\u0200\u0202\u1EA0\u1EAC\u1EB6\u1E00\u0104\u023A\u2C6F]/g }, { 'base': 'AA', 'letters': /[\uA732]/g }, { 'base': 'AE', 'letters': /[\u00C6\u01FC\u01E2]/g }, { 'base': 'AO', 'letters': /[\uA734]/g }, { 'base': 'AU', 'letters': /[\uA736]/g }, { 'base': 'AV', 'letters': /[\uA738\uA73A]/g }, { 'base': 'AY', 'letters': /[\uA73C]/g }, { 'base': 'B', 'letters': /[\u0042\u24B7\uFF22\u1E02\u1E04\u1E06\u0243\u0182\u0181]/g }, { 'base': 'C', 'letters': /[\u0043\u24B8\uFF23\u0106\u0108\u010A\u010C\u00C7\u1E08\u0187\u023B\uA73E]/g }, { 'base': 'D', 'letters': /[\u0044\u24B9\uFF24\u1E0A\u010E\u1E0C\u1E10\u1E12\u1E0E\u0110\u018B\u018A\u0189\uA779]/g }, { 'base': 'DZ', 'letters': /[\u01F1\u01C4]/g }, { 'base': 'Dz', 'letters': /[\u01F2\u01C5]/g }, { 'base': 'E', 'letters': /[\u0045\u24BA\uFF25\u00C8\u00C9\u00CA\u1EC0\u1EBE\u1EC4\u1EC2\u1EBC\u0112\u1E14\u1E16\u0114\u0116\u00CB\u1EBA\u011A\u0204\u0206\u1EB8\u1EC6\u0228\u1E1C\u0118\u1E18\u1E1A\u0190\u018E]/g }, { 'base': 'F', 'letters': /[\u0046\u24BB\uFF26\u1E1E\u0191\uA77B]/g }, { 'base': 'G', 'letters': /[\u0047\u24BC\uFF27\u01F4\u011C\u1E20\u011E\u0120\u01E6\u0122\u01E4\u0193\uA7A0\uA77D\uA77E]/g }, { 'base': 'H', 'letters': /[\u0048\u24BD\uFF28\u0124\u1E22\u1E26\u021E\u1E24\u1E28\u1E2A\u0126\u2C67\u2C75\uA78D]/g }, { 'base': 'I', 'letters': /[\u0049\u24BE\uFF29\u00CC\u00CD\u00CE\u0128\u012A\u012C\u0130\u00CF\u1E2E\u1EC8\u01CF\u0208\u020A\u1ECA\u012E\u1E2C\u0197]/g }, { 'base': 'J', 'letters': /[\u004A\u24BF\uFF2A\u0134\u0248]/g }, { 'base': 'K', 'letters': /[\u004B\u24C0\uFF2B\u1E30\u01E8\u1E32\u0136\u1E34\u0198\u2C69\uA740\uA742\uA744\uA7A2]/g }, { 'base': 'L', 'letters': /[\u004C\u24C1\uFF2C\u013F\u0139\u013D\u1E36\u1E38\u013B\u1E3C\u1E3A\u0141\u023D\u2C62\u2C60\uA748\uA746\uA780]/g }, { 'base': 'LJ', 'letters': /[\u01C7]/g }, { 'base': 'Lj', 'letters': /[\u01C8]/g }, { 'base': 'M', 'letters': /[\u004D\u24C2\uFF2D\u1E3E\u1E40\u1E42\u2C6E\u019C]/g }, { 'base': 'N', 'letters': /[\u004E\u24C3\uFF2E\u01F8\u0143\u00D1\u1E44\u0147\u1E46\u0145\u1E4A\u1E48\u0220\u019D\uA790\uA7A4]/g }, { 'base': 'NJ', 'letters': /[\u01CA]/g }, { 'base': 'Nj', 'letters': /[\u01CB]/g }, { 'base': 'O', 'letters': /[\u004F\u24C4\uFF2F\u00D2\u00D3\u00D4\u1ED2\u1ED0\u1ED6\u1ED4\u00D5\u1E4C\u022C\u1E4E\u014C\u1E50\u1E52\u014E\u022E\u0230\u00D6\u022A\u1ECE\u0150\u01D1\u020C\u020E\u01A0\u1EDC\u1EDA\u1EE0\u1EDE\u1EE2\u1ECC\u1ED8\u01EA\u01EC\u00D8\u01FE\u0186\u019F\uA74A\uA74C]/g }, { 'base': 'OI', 'letters': /[\u01A2]/g }, { 'base': 'OO', 'letters': /[\uA74E]/g }, { 'base': 'OU', 'letters': /[\u0222]/g }, { 'base': 'P', 'letters': /[\u0050\u24C5\uFF30\u1E54\u1E56\u01A4\u2C63\uA750\uA752\uA754]/g }, { 'base': 'Q', 'letters': /[\u0051\u24C6\uFF31\uA756\uA758\u024A]/g }, { 'base': 'R', 'letters': /[\u0052\u24C7\uFF32\u0154\u1E58\u0158\u0210\u0212\u1E5A\u1E5C\u0156\u1E5E\u024C\u2C64\uA75A\uA7A6\uA782]/g }, { 'base': 'S', 'letters': /[\u0053\u24C8\uFF33\u1E9E\u015A\u1E64\u015C\u1E60\u0160\u1E66\u1E62\u1E68\u0218\u015E\u2C7E\uA7A8\uA784]/g }, { 'base': 'T', 'letters': /[\u0054\u24C9\uFF34\u1E6A\u0164\u1E6C\u021A\u0162\u1E70\u1E6E\u0166\u01AC\u01AE\u023E\uA786]/g }, { 'base': 'TZ', 'letters': /[\uA728]/g }, { 'base': 'U', 'letters': /[\u0055\u24CA\uFF35\u00D9\u00DA\u00DB\u0168\u1E78\u016A\u1E7A\u016C\u00DC\u01DB\u01D7\u01D5\u01D9\u1EE6\u016E\u0170\u01D3\u0214\u0216\u01AF\u1EEA\u1EE8\u1EEE\u1EEC\u1EF0\u1EE4\u1E72\u0172\u1E76\u1E74\u0244]/g }, { 'base': 'V', 'letters': /[\u0056\u24CB\uFF36\u1E7C\u1E7E\u01B2\uA75E\u0245]/g }, { 'base': 'VY', 'letters': /[\uA760]/g }, { 'base': 'W', 'letters': /[\u0057\u24CC\uFF37\u1E80\u1E82\u0174\u1E86\u1E84\u1E88\u2C72]/g }, { 'base': 'X', 'letters': /[\u0058\u24CD\uFF38\u1E8A\u1E8C]/g }, { 'base': 'Y', 'letters': /[\u0059\u24CE\uFF39\u1EF2\u00DD\u0176\u1EF8\u0232\u1E8E\u0178\u1EF6\u1EF4\u01B3\u024E\u1EFE]/g }, { 'base': 'Z', 'letters': /[\u005A\u24CF\uFF3A\u0179\u1E90\u017B\u017D\u1E92\u1E94\u01B5\u0224\u2C7F\u2C6B\uA762]/g }, { 'base': 'a', 'letters': /[\u0061\u24D0\uFF41\u1E9A\u00E0\u00E1\u00E2\u1EA7\u1EA5\u1EAB\u1EA9\u00E3\u0101\u0103\u1EB1\u1EAF\u1EB5\u1EB3\u0227\u01E1\u00E4\u01DF\u1EA3\u00E5\u01FB\u01CE\u0201\u0203\u1EA1\u1EAD\u1EB7\u1E01\u0105\u2C65\u0250]/g }, { 'base': 'aa', 'letters': /[\uA733]/g }, { 'base': 'ae', 'letters': /[\u00E6\u01FD\u01E3]/g }, { 'base': 'ao', 'letters': /[\uA735]/g }, { 'base': 'au', 'letters': /[\uA737]/g }, { 'base': 'av', 'letters': /[\uA739\uA73B]/g }, { 'base': 'ay', 'letters': /[\uA73D]/g }, { 'base': 'b', 'letters': /[\u0062\u24D1\uFF42\u1E03\u1E05\u1E07\u0180\u0183\u0253]/g }, { 'base': 'c', 'letters': /[\u0063\u24D2\uFF43\u0107\u0109\u010B\u010D\u00E7\u1E09\u0188\u023C\uA73F\u2184]/g }, { 'base': 'd', 'letters': /[\u0064\u24D3\uFF44\u1E0B\u010F\u1E0D\u1E11\u1E13\u1E0F\u0111\u018C\u0256\u0257\uA77A]/g }, { 'base': 'dz', 'letters': /[\u01F3\u01C6]/g }, { 'base': 'e', 'letters': /[\u0065\u24D4\uFF45\u00E8\u00E9\u00EA\u1EC1\u1EBF\u1EC5\u1EC3\u1EBD\u0113\u1E15\u1E17\u0115\u0117\u00EB\u1EBB\u011B\u0205\u0207\u1EB9\u1EC7\u0229\u1E1D\u0119\u1E19\u1E1B\u0247\u025B\u01DD]/g }, { 'base': 'f', 'letters': /[\u0066\u24D5\uFF46\u1E1F\u0192\uA77C]/g }, { 'base': 'g', 'letters': /[\u0067\u24D6\uFF47\u01F5\u011D\u1E21\u011F\u0121\u01E7\u0123\u01E5\u0260\uA7A1\u1D79\uA77F]/g }, { 'base': 'h', 'letters': /[\u0068\u24D7\uFF48\u0125\u1E23\u1E27\u021F\u1E25\u1E29\u1E2B\u1E96\u0127\u2C68\u2C76\u0265]/g }, { 'base': 'hv', 'letters': /[\u0195]/g }, { 'base': 'i', 'letters': /[\u0069\u24D8\uFF49\u00EC\u00ED\u00EE\u0129\u012B\u012D\u00EF\u1E2F\u1EC9\u01D0\u0209\u020B\u1ECB\u012F\u1E2D\u0268\u0131]/g }, { 'base': 'j', 'letters': /[\u006A\u24D9\uFF4A\u0135\u01F0\u0249]/g }, { 'base': 'k', 'letters': /[\u006B\u24DA\uFF4B\u1E31\u01E9\u1E33\u0137\u1E35\u0199\u2C6A\uA741\uA743\uA745\uA7A3]/g }, { 'base': 'l', 'letters': /[\u006C\u24DB\uFF4C\u0140\u013A\u013E\u1E37\u1E39\u013C\u1E3D\u1E3B\u017F\u0142\u019A\u026B\u2C61\uA749\uA781\uA747]/g }, { 'base': 'lj', 'letters': /[\u01C9]/g }, { 'base': 'm', 'letters': /[\u006D\u24DC\uFF4D\u1E3F\u1E41\u1E43\u0271\u026F]/g }, { 'base': 'n', 'letters': /[\u006E\u24DD\uFF4E\u01F9\u0144\u00F1\u1E45\u0148\u1E47\u0146\u1E4B\u1E49\u019E\u0272\u0149\uA791\uA7A5]/g }, { 'base': 'nj', 'letters': /[\u01CC]/g }, { 'base': 'o', 'letters': /[\u006F\u24DE\uFF4F\u00F2\u00F3\u00F4\u1ED3\u1ED1\u1ED7\u1ED5\u00F5\u1E4D\u022D\u1E4F\u014D\u1E51\u1E53\u014F\u022F\u0231\u00F6\u022B\u1ECF\u0151\u01D2\u020D\u020F\u01A1\u1EDD\u1EDB\u1EE1\u1EDF\u1EE3\u1ECD\u1ED9\u01EB\u01ED\u00F8\u01FF\u0254\uA74B\uA74D\u0275]/g }, { 'base': 'oi', 'letters': /[\u01A3]/g }, { 'base': 'ou', 'letters': /[\u0223]/g }, { 'base': 'oo', 'letters': /[\uA74F]/g }, { 'base': 'p', 'letters': /[\u0070\u24DF\uFF50\u1E55\u1E57\u01A5\u1D7D\uA751\uA753\uA755]/g }, { 'base': 'q', 'letters': /[\u0071\u24E0\uFF51\u024B\uA757\uA759]/g }, { 'base': 'r', 'letters': /[\u0072\u24E1\uFF52\u0155\u1E59\u0159\u0211\u0213\u1E5B\u1E5D\u0157\u1E5F\u024D\u027D\uA75B\uA7A7\uA783]/g }, { 'base': 's', 'letters': /[\u0073\u24E2\uFF53\u00DF\u015B\u1E65\u015D\u1E61\u0161\u1E67\u1E63\u1E69\u0219\u015F\u023F\uA7A9\uA785\u1E9B]/g }, { 'base': 't', 'letters': /[\u0074\u24E3\uFF54\u1E6B\u1E97\u0165\u1E6D\u021B\u0163\u1E71\u1E6F\u0167\u01AD\u0288\u2C66\uA787]/g }, { 'base': 'tz', 'letters': /[\uA729]/g }, { 'base': 'u', 'letters': /[\u0075\u24E4\uFF55\u00F9\u00FA\u00FB\u0169\u1E79\u016B\u1E7B\u016D\u00FC\u01DC\u01D8\u01D6\u01DA\u1EE7\u016F\u0171\u01D4\u0215\u0217\u01B0\u1EEB\u1EE9\u1EEF\u1EED\u1EF1\u1EE5\u1E73\u0173\u1E77\u1E75\u0289]/g }, { 'base': 'v', 'letters': /[\u0076\u24E5\uFF56\u1E7D\u1E7F\u028B\uA75F\u028C]/g }, { 'base': 'vy', 'letters': /[\uA761]/g }, { 'base': 'w', 'letters': /[\u0077\u24E6\uFF57\u1E81\u1E83\u0175\u1E87\u1E85\u1E98\u1E89\u2C73]/g }, { 'base': 'x', 'letters': /[\u0078\u24E7\uFF58\u1E8B\u1E8D]/g }, { 'base': 'y', 'letters': /[\u0079\u24E8\uFF59\u1EF3\u00FD\u0177\u1EF9\u0233\u1E8F\u00FF\u1EF7\u1E99\u1EF5\u01B4\u024F\u1EFF]/g }, { 'base': 'z', 'letters': /[\u007A\u24E9\uFF5A\u017A\u1E91\u017C\u017E\u1E93\u1E95\u01B6\u0225\u0240\u2C6C\uA763]/g }];
7 |
8 | var stripDiacritics = function stripDiacritics(str) {
9 | for (var i = 0; i < map.length; i++) {
10 | str = str.replace(map[i].letters, map[i].base);
11 | }
12 | return str;
13 | };
14 |
15 | exports.default = stripDiacritics;
--------------------------------------------------------------------------------
/lib/utils/trim.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | var trim = function trim(str) {
7 | return str.replace(/^\s+|\s+$/g, '');
8 | };
9 |
10 | exports.default = trim;
--------------------------------------------------------------------------------
/package-scripts.js:
--------------------------------------------------------------------------------
1 | const npsUtils = require('nps-utils');
2 | const path = require('path');
3 | const series = npsUtils.series;
4 | const rimraf = npsUtils.rimraf;
5 | const concurrent = npsUtils.concurrent;
6 |
7 | module.exports = {
8 | scripts: {
9 | build: {
10 | description: 'clean dist directory and run all builds',
11 | default: series(
12 | rimraf('dist'),
13 | rimraf('lib'),
14 | concurrent.nps('build.css', 'build.cssmin'),
15 | concurrent.nps('build.rollup', 'build.babel')
16 | ),
17 | rollup: 'rollup --config',
18 | babel: 'babel src -d lib',
19 | css: 'lessc less/default.less dist/react-select-plus.css',
20 | cssmin: 'lessc --clean-css less/default.less dist/react-select-plus.min.css',
21 | standalone: series(
22 | 'cp examples/src/standalone.html examples/dist/standalone.html',
23 | 'lessc examples/src/example.less examples/dist/example.css'
24 | ),
25 | },
26 | publish: {
27 | default: series(
28 | rimraf('examples/dist'),
29 | 'webpack --progress -p',
30 | 'cp examples/src/.gitignore examples/dist/.gitignore',
31 | 'gh-pages -d examples/dist'
32 | ),
33 | },
34 | },
35 | };
36 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-select-plus",
3 | "version": "1.2.0",
4 | "description": "A fork of react-select with support for option groups",
5 | "main": "lib/index.js",
6 | "jsnext:main": "dist/react-select-plus.es.js",
7 | "module": "dist/react-select-plus.es.js",
8 | "style": "dist/react-select-plus.min.css",
9 | "author": "HubSpot",
10 | "license": "MIT",
11 | "repository": {
12 | "type": "git",
13 | "url": "https://github.com/HubSpot/react-select-plus.git"
14 | },
15 | "dependencies": {
16 | "classnames": "^2.2.4",
17 | "prop-types": "^15.5.8",
18 | "react-input-autosize": "^2.1.2"
19 | },
20 | "devDependencies": {
21 | "babel-cli": "^6.26.0",
22 | "babel-core": "^6.26.0",
23 | "babel-loader": "^7.1.1",
24 | "babel-plugin-external-helpers": "^6.22.0",
25 | "babel-plugin-istanbul": "^4.1.4",
26 | "babel-plugin-transform-react-remove-prop-types": "^0.4.8",
27 | "babel-preset-env": "^1.6.1",
28 | "babel-preset-react": "^6.24.1",
29 | "babel-preset-stage-0": "^6.24.1",
30 | "babel-register": "^6.26.0",
31 | "chai": "^4.1.2",
32 | "coveralls": "^2.11.12",
33 | "create-react-class": "^15.5.2",
34 | "cross-env": "^5.0.5",
35 | "css-loader": "^0.28.7",
36 | "eslint": "^4.6.1",
37 | "eslint-plugin-react": "^7.3.0",
38 | "extract-text-webpack-plugin": "^3.0.0",
39 | "gh-pages": "^1.1.0",
40 | "html-loader": "^0.5.1",
41 | "html-webpack-plugin": "^2.30.1",
42 | "husky": "^0.14.3",
43 | "isomorphic-fetch": "^2.2.1",
44 | "istanbul": "^0.4.5",
45 | "jsdom": "^9.12.0",
46 | "less": "^2.7.2",
47 | "less-loader": "^4.0.5",
48 | "less-plugin-clean-css": "^1.5.1",
49 | "lint-staged": "^4.3.0",
50 | "mocha": "^3.0.2",
51 | "nps": "^5.7.1",
52 | "nps-utils": "^1.3.0",
53 | "nyc": "^11.1.0",
54 | "react": "^15.5.0",
55 | "react-addons-shallow-compare": "^15.5.0",
56 | "react-dom": "^15.5.0",
57 | "react-gravatar": "^2.4.5",
58 | "react-highlight-words": "^0.8.1",
59 | "react-test-renderer": "^15.6.1",
60 | "react-virtualized": "^9.9.0",
61 | "react-virtualized-select": "^3.1.0",
62 | "rollup": "^0.49.2",
63 | "rollup-plugin-babel": "^3.0.2",
64 | "rollup-plugin-commonjs": "^8.2.0",
65 | "rollup-plugin-node-resolve": "^3.0.0",
66 | "rollup-plugin-uglify": "^2.0.1",
67 | "sinon": "^3.2.1",
68 | "style-loader": "^0.18.2",
69 | "uglify-es": "^3.0.28",
70 | "unexpected": "^10.35.0",
71 | "unexpected-dom": "^4.0.0",
72 | "unexpected-react": "^4.1.0",
73 | "unexpected-sinon": "^10.4.0",
74 | "webpack": "^3.5.5",
75 | "webpack-dev-server": "^2.7.1"
76 | },
77 | "peerDependencies": {
78 | "react": "^0.14.9 || ^15.3.0 || ^16.0.0-rc || ^16.0",
79 | "react-dom": "^0.14.9 || ^15.3.0 || ^16.0.0-rc || ^16.0"
80 | },
81 | "scripts": {
82 | "build": "nps build",
83 | "cover": "cross-env NODE_ENV=test nyc --reporter=lcov --reporter=text mocha",
84 | "coveralls": "cross-env NODE_ENV=test nyc --reporter=lcov --reporter=text mocha && cat coverage/lcov.info | coveralls",
85 | "lint": "eslint .",
86 | "deploy": "cross-env NODE_ENV=production nps publish",
87 | "start": "webpack-dev-server --progress",
88 | "test": "cross-env NODE_ENV=test mocha --compilers js:babel-core/register",
89 | "precommit": "lint-staged && yarn run test"
90 | },
91 | "files": [
92 | "dist",
93 | "less",
94 | "lib",
95 | "scss"
96 | ],
97 | "keywords": [
98 | "combobox",
99 | "form",
100 | "input",
101 | "multiselect",
102 | "react",
103 | "react-component",
104 | "select",
105 | "ui"
106 | ],
107 | "lint-staged": {
108 | "*.js": "eslint"
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/rollup.config.js:
--------------------------------------------------------------------------------
1 | import babel from 'rollup-plugin-babel';
2 | import resolve from 'rollup-plugin-node-resolve';
3 | import uglify from 'rollup-plugin-uglify';
4 | import { minify } from 'uglify-es';
5 |
6 | const name = 'Select';
7 | const path = 'dist/react-select-plus';
8 | const globals = {
9 | classnames: 'classNames',
10 | 'prop-types': 'PropTypes',
11 | 'react-dom': 'ReactDOM',
12 | 'react-input-autosize': 'AutosizeInput',
13 | react: 'React',
14 | };
15 | const external = Object.keys(globals);
16 | const babelOptions = (production) => {
17 | let result = {
18 | babelrc: false,
19 | presets: [['env', { modules: false }], 'stage-0', 'react'],
20 | plugins: ['external-helpers'],
21 | };
22 | if (production) {
23 | result.plugins.push('transform-react-remove-prop-types');
24 | };
25 | return result;
26 | };
27 |
28 | export default [
29 | {
30 | input: 'src/index.js',
31 | output: {
32 | file: path + '.es.js',
33 | format: 'es',
34 | },
35 | external: external,
36 | plugins: [babel(babelOptions(false))],
37 | },
38 | {
39 | input: 'src/index.umd.js',
40 | output: {
41 | name: name,
42 | file: path + '.js',
43 | format: 'umd',
44 | },
45 | globals: globals,
46 | external: external,
47 | plugins: [babel(babelOptions(false)), resolve()],
48 | },
49 | {
50 | input: 'src/index.umd.js',
51 | output: {
52 | name: name,
53 | file: path + '.min.js',
54 | format: 'umd',
55 | },
56 | globals: globals,
57 | external: external,
58 | plugins: [babel(babelOptions(true)), resolve(), uglify({}, minify)],
59 | },
60 | ];
61 |
--------------------------------------------------------------------------------
/scss/components.scss:
--------------------------------------------------------------------------------
1 | @import "control";
2 | @import "menu";
3 | @import "mixins";
4 | @import "multi";
5 | @import "spinner";
6 |
--------------------------------------------------------------------------------
/scss/control.scss:
--------------------------------------------------------------------------------
1 | //
2 | // Control
3 | // ------------------------------
4 |
5 | @import 'spinner';
6 | @import 'mixins';
7 |
8 | .Select {
9 | position: relative;
10 |
11 | // disable some browser-specific behaviours that break the input
12 | input::-webkit-contacts-auto-fill-button,
13 | input::-webkit-credentials-auto-fill-button {
14 | display: none !important;
15 | }
16 | input::-ms-clear {
17 | display: none !important;
18 | }
19 | input::-ms-reveal {
20 | display: none !important;
21 | }
22 |
23 | // preferred box model
24 | &,
25 | & div,
26 | & input,
27 | & span {
28 | @include box-sizing(border-box);
29 | }
30 |
31 | // handle disabled state
32 | &.is-disabled {
33 | .Select-arrow-zone {
34 | cursor: default;
35 | pointer-events: none;
36 | }
37 |
38 | > .Select-control {
39 | background-color: $select-input-bg-disabled;
40 | &:hover {
41 | box-shadow: none;
42 | }
43 | }
44 | }
45 |
46 | &.is-searchable {
47 | &.is-open > .Select-control {
48 | cursor: text;
49 | }
50 |
51 | &.is-focused:not(.is-open) > .Select-control {
52 | cursor: text;
53 | }
54 | }
55 |
56 | &.is-open > .Select-control {
57 | @include border-bottom-radius( 0 );
58 | background: $select-input-bg;
59 | border-color: darken($select-input-border-color, 10%) $select-input-border-color lighten($select-input-border-color, 5%);
60 |
61 | // flip the arrow so its pointing up when the menu is open
62 | .Select-arrow {
63 | top: -2px;
64 | border-color: transparent transparent $select-arrow-color;
65 | border-width: 0 $select-arrow-width $select-arrow-width;
66 | }
67 | }
68 |
69 | &.is-focused > .Select-control {
70 | background: $select-input-bg-focus;
71 | }
72 |
73 | &.is-focused:not(.is-open) > .Select-control {
74 | border-color: $select-input-border-focus lighten($select-input-border-focus, 5%) lighten($select-input-border-focus, 5%);
75 | box-shadow: $select-input-box-shadow-focus;
76 | }
77 |
78 |
79 | &.has-value.is-clearable.Select--single > .Select-control .Select-value {
80 | padding-right: ($select-clear-width + $select-arrow-width * 5);
81 | }
82 |
83 | &.has-value.Select--single > .Select-control .Select-value,
84 | &.has-value.is-pseudo-focused.Select--single > .Select-control .Select-value {
85 | .Select-value-label {
86 | color: $select-text-color;
87 | }
88 | a.Select-value-label {
89 | cursor: pointer;
90 | text-decoration: none;
91 |
92 | &:hover,
93 | &:focus {
94 | color: $select-link-hover-color;
95 | outline: none;
96 | text-decoration: underline;
97 | }
98 |
99 | &:focus {
100 | background: $select-input-bg-focus;
101 | }
102 | }
103 | }
104 |
105 | // fake-hide the input when the control is pseudo-focused
106 | &.has-value.is-pseudo-focused .Select-input {
107 | opacity: 0;
108 | }
109 |
110 | &.is-open .Select-arrow,
111 | .Select-arrow-zone:hover > .Select-arrow {
112 | border-top-color: $select-arrow-color-hover;
113 | }
114 | &.Select--rtl {
115 | direction: rtl;
116 | text-align: right;
117 | }
118 | }
119 |
120 | // base
121 |
122 | .Select-control {
123 | background-color: $select-input-bg;
124 | border-color: lighten($select-input-border-color, 5%) $select-input-border-color darken($select-input-border-color, 10%);
125 | border-radius: $select-input-border-radius;
126 | border: $select-input-border-width solid $select-input-border-color;
127 | color: $select-text-color;
128 | cursor: default;
129 | display: table;
130 | border-spacing: 0;
131 | border-collapse: separate;
132 | height: $select-input-height;
133 | outline: none;
134 | overflow: hidden;
135 | position: relative;
136 | width: 100%;
137 |
138 | &:hover {
139 | box-shadow: $select-input-hover-box-shadow;
140 | }
141 |
142 | .Select-input:focus {
143 | outline: none;
144 | background: $select-input-bg-focus;
145 | }
146 | }
147 |
148 | // placeholder
149 | .Select-placeholder,
150 | .Select--single > .Select-control .Select-value {
151 | bottom: 0;
152 | color: $select-input-placeholder;
153 | left: 0;
154 | line-height: $select-input-internal-height;
155 | padding-left: $select-padding-horizontal;
156 | padding-right: $select-padding-horizontal;
157 | position: absolute;
158 | right: 0;
159 | top: 0;
160 |
161 | // crop text
162 | max-width: 100%;
163 | overflow: hidden;
164 | text-overflow: ellipsis;
165 | white-space: nowrap;
166 | }
167 |
168 |
169 | // the element users type in
170 |
171 | .Select-input {
172 | // inherits `display: inline-block` from "react-input-autosize"
173 | height: $select-input-internal-height;
174 | padding-left: $select-padding-horizontal;
175 | padding-right: $select-padding-horizontal;
176 | vertical-align: middle;
177 |
178 | > input {
179 | background: none transparent;
180 | border: 0 none;
181 | box-shadow: none;
182 | cursor: default;
183 | display: inline-block;
184 | font-family: inherit;
185 | font-size: inherit;
186 | // height: $select-input-internal-height;
187 | margin: 0;
188 | outline: none;
189 | // padding: 0;
190 | line-height: 17px; /* For IE 8 compatibility */
191 | padding: (($select-input-internal-height - 14) / 2 - 2) 0 (($select-input-internal-height - 14) / 2 + 2); /* For IE 8 compatibility */
192 | -webkit-appearance: none;
193 |
194 | .is-focused & {
195 | cursor: text;
196 | }
197 | }
198 |
199 | }
200 |
201 | // fake input
202 | .Select-control:not(.is-searchable) > .Select-input {
203 | outline: none;
204 | }
205 |
206 | // loading indicator
207 | .Select-loading-zone {
208 | cursor: pointer;
209 | display: table-cell;
210 | position: relative;
211 | text-align: center;
212 | vertical-align: middle;
213 | width: $select-loading-size;
214 | }
215 | .Select-loading {
216 | @include Select-spinner($select-loading-size, $select-loading-color-bg, $select-loading-color);
217 | vertical-align: middle;
218 | }
219 |
220 |
221 | // the little cross that clears the field
222 |
223 | .Select-clear-zone {
224 | @include animation( Select-animation-fadeIn 200ms );
225 | color: $select-clear-color;
226 | cursor: pointer;
227 | display: table-cell;
228 | position: relative;
229 | text-align: center;
230 | vertical-align: middle;
231 | width: $select-clear-width;
232 |
233 | &:hover {
234 | color: $select-clear-hover-color;
235 | }
236 | }
237 | .Select-clear {
238 | display: inline-block;
239 | font-size: $select-clear-size;
240 | line-height: 1;
241 | }
242 | .Select--multi .Select-clear-zone {
243 | width: $select-clear-width;
244 | }
245 |
246 | .Select--multi .Select-multi-value-wrapper {
247 | display: inline-block;
248 | }
249 | .Select .Select-aria-only {
250 | position: absolute;
251 | display: inline-block;
252 | height: 1px;
253 | width: 1px;
254 | margin: -1px;
255 | clip: rect(0,0,0,0);
256 | overflow: hidden;
257 | float: left;
258 | }
259 |
260 |
261 | // arrow indicator
262 |
263 | .Select-arrow-zone {
264 | cursor: pointer;
265 | display: table-cell;
266 | position: relative;
267 | text-align: center;
268 | vertical-align: middle;
269 | width: ($select-arrow-width * 5);
270 | padding-right: $select-arrow-width;
271 |
272 | .Select--rtl & {
273 | padding-right: 0;
274 | padding-left: $select-arrow-width;
275 | }
276 | }
277 |
278 | .Select-arrow {
279 | border-color: $select-arrow-color transparent transparent;
280 | border-style: solid;
281 | border-width: $select-arrow-width $select-arrow-width ($select-arrow-width / 2);
282 | display: inline-block;
283 | height: 0;
284 | width: 0;
285 | position: relative;
286 | }
287 |
288 |
289 |
290 |
291 | // Animation
292 | // ------------------------------
293 |
294 | // fade in
295 |
296 | @-webkit-keyframes Select-animation-fadeIn {
297 | from { opacity: 0; }
298 | to { opacity: 1; }
299 | }
300 | @keyframes Select-animation-fadeIn {
301 | from { opacity: 0; }
302 | to { opacity: 1; }
303 | }
304 |
--------------------------------------------------------------------------------
/scss/default.scss:
--------------------------------------------------------------------------------
1 | @import "select";
2 | @import "control";
3 | @import "menu";
4 | @import "mixins";
5 | @import "multi";
6 | @import "spinner";
7 |
--------------------------------------------------------------------------------
/scss/menu.scss:
--------------------------------------------------------------------------------
1 | //
2 | // Select Menu
3 | // ------------------------------
4 |
5 |
6 | // wrapper around the menu
7 |
8 | .Select-menu-outer {
9 | // Unfortunately, having both border-radius and allows scrolling using overflow defined on the same
10 | // element forces the browser to repaint on scroll. However, if these definitions are split into an
11 | // outer and an inner element, the browser is able to optimize the scrolling behavior and does not
12 | // have to repaint on scroll.
13 | @include border-bottom-radius( $select-input-border-radius );
14 | background-color: $select-input-bg;
15 | border: 1px solid $select-input-border-color;
16 | border-top-color: mix($select-input-bg, $select-input-border-color, 50%);
17 | box-shadow: $select-menu-box-shadow;
18 | box-sizing: border-box;
19 | margin-top: -1px;
20 | max-height: $select-menu-max-height;
21 | position: absolute;
22 | top: 100%;
23 | width: 100%;
24 | z-index: $select-menu-zindex;
25 | -webkit-overflow-scrolling: touch;
26 | }
27 |
28 |
29 | // wrapper
30 |
31 | .Select-menu {
32 | max-height: ($select-menu-max-height - 2px);
33 | overflow-y: auto;
34 | }
35 |
36 |
37 | // options
38 |
39 | .Select-option {
40 | box-sizing: border-box;
41 | background-color: $select-option-bg;
42 | color: $select-option-color;
43 | cursor: pointer;
44 | display: block;
45 | padding: $select-padding-vertical $select-padding-horizontal;
46 |
47 | &:last-child {
48 | @include border-bottom-radius( $select-input-border-radius );
49 | }
50 |
51 | &.is-selected {
52 | background-color: $select-option-selected-bg;
53 | color: $select-option-selected-color;
54 | }
55 |
56 | &.is-focused {
57 | background-color: $select-option-focused-bg;
58 | color: $select-option-focused-color;
59 | }
60 |
61 | &.is-disabled {
62 | color: $select-option-disabled-color;
63 | cursor: default;
64 | }
65 |
66 | }
67 |
68 |
69 | // no results
70 |
71 | .Select-noresults {
72 | box-sizing: border-box;
73 | color: $select-noresults-color;
74 | cursor: default;
75 | display: block;
76 | padding: $select-padding-vertical $select-padding-horizontal;
77 | }
78 |
--------------------------------------------------------------------------------
/scss/mixins.scss:
--------------------------------------------------------------------------------
1 | //
2 | // Mixins
3 | // ------------------------------
4 |
5 |
6 | // Utilities
7 |
8 | @mixin size($width, $height)
9 | {
10 | width: $width;
11 | height: $height;
12 | }
13 | @mixin square($size)
14 | {
15 | @include size($size, $size);
16 | }
17 | @mixin border-top-radius($radius)
18 | {
19 | border-top-right-radius: $radius;
20 | border-top-left-radius: $radius;
21 | }
22 | @mixin border-right-radius($radius)
23 | {
24 | border-bottom-right-radius: $radius;
25 | border-top-right-radius: $radius;
26 | }
27 | @mixin border-bottom-radius($radius)
28 | {
29 | border-bottom-right-radius: $radius;
30 | border-bottom-left-radius: $radius;
31 | }
32 | @mixin border-left-radius($radius)
33 | {
34 | border-bottom-left-radius: $radius;
35 | border-top-left-radius: $radius;
36 | }
37 |
38 |
39 | // Vendor Prefixes
40 |
41 | @mixin animation($animation)
42 | {
43 | -webkit-animation: $animation;
44 | -o-animation: $animation;
45 | animation: $animation;
46 | }
47 | @mixin box-sizing($boxmodel)
48 | {
49 | -webkit-box-sizing: $boxmodel;
50 | -moz-box-sizing: $boxmodel;
51 | box-sizing: $boxmodel;
52 | }
53 |
--------------------------------------------------------------------------------
/scss/multi.scss:
--------------------------------------------------------------------------------
1 | //
2 | // Multi-Select
3 | // ------------------------------
4 |
5 |
6 | // Base
7 |
8 | .Select--multi {
9 |
10 | // add margin to the input element
11 | .Select-input {
12 | vertical-align: middle;
13 | // border: 1px solid transparent;
14 | margin-left: $select-padding-horizontal;
15 | padding: 0;
16 | }
17 | &.Select--rtl .Select-input {
18 | margin-left: 0;
19 | margin-right: $select-padding-horizontal;
20 | }
21 |
22 | // reduce margin once there is value
23 | &.has-value .Select-input {
24 | margin-left: $select-item-gutter;
25 | }
26 |
27 | // Items
28 | .Select-value {
29 | background-color: $select-item-bg;
30 | border-radius: $select-item-border-radius;
31 | border: 1px solid $select-item-border-color;
32 | color: $select-item-color;
33 | display: inline-block;
34 | font-size: $select-item-font-size;
35 | margin-left: $select-item-gutter;
36 | margin-top: $select-item-gutter;
37 | vertical-align: top;
38 | }
39 |
40 | // common
41 | .Select-value-icon,
42 | .Select-value-label {
43 | display: inline-block;
44 | vertical-align: middle;
45 | }
46 |
47 | // label
48 | .Select-value-label {
49 | @include border-right-radius( $select-item-border-radius );
50 | cursor: default;
51 | padding: $select-item-padding-vertical $select-item-padding-horizontal;
52 | }
53 | a.Select-value-label {
54 | color: $select-item-color;
55 | cursor: pointer;
56 | text-decoration: none;
57 |
58 | &:hover {
59 | text-decoration: underline;
60 | }
61 | }
62 |
63 | // icon
64 | .Select-value-icon {
65 | cursor: pointer;
66 | @include border-left-radius( $select-item-border-radius );
67 | border-right: 1px solid $select-item-border-color;
68 |
69 | // move the baseline up by 1px
70 | padding: ($select-item-padding-vertical - 1) $select-item-padding-horizontal ($select-item-padding-vertical + 1);
71 |
72 | &:hover,
73 | &:focus {
74 | background-color: $select-item-hover-bg;
75 | color: $select-item-hover-color;
76 | }
77 | &:active {
78 | background-color: $select-item-border-color;
79 | }
80 | }
81 |
82 | &.Select--rtl {
83 | .Select-value {
84 | margin-left: 0;
85 | margin-right: $select-item-gutter;
86 | }
87 | .Select-value-icon {
88 | border-right: none;
89 | border-left: 1px solid $select-item-border-color;
90 | }
91 | }
92 | }
93 |
94 | .Select--multi.is-disabled {
95 | .Select-value {
96 | background-color: $select-item-disabled-bg;
97 | border: 1px solid $select-item-disabled-border-color;
98 | color: $select-item-disabled-color;
99 | }
100 | // icon
101 | .Select-value-icon {
102 | cursor: not-allowed;
103 | border-right: 1px solid $select-item-disabled-border-color;
104 |
105 | &:hover,
106 | &:focus,
107 | &:active {
108 | background-color: $select-item-disabled-bg;
109 | }
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/scss/select.scss:
--------------------------------------------------------------------------------
1 | /**
2 | * React Select Plus
3 | * =================
4 | * MIT License: https://github.com/HubSpot/react-select-plus
5 | */
6 |
7 | // Variables
8 | // ------------------------------
9 |
10 | // control options
11 | $select-input-bg: #fff !default;
12 | $select-input-bg-disabled: #f9f9f9 !default;
13 | $select-input-bg-focus: $select-input-bg !default;
14 | $select-input-border-color: #ccc !default;
15 | $select-input-border-radius: 4px !default;
16 | $select-input-border-focus: #08c !default; // blue
17 | $select-input-box-shadow-focus: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 0 5px -1px fade($select-input-border-focus, 50%) !default;
18 | $select-input-border-width: 1px !default;
19 | $select-input-height: 36px !default;
20 | $select-input-internal-height: ($select-input-height - ($select-input-border-width * 2)) !default;
21 | $select-input-placeholder: #aaa !default;
22 | $select-text-color: #333 !default;
23 | $select-link-hover-color: $select-input-border-focus !default;
24 | $select-input-hover-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.06) !default;
25 |
26 | $select-padding-vertical: 8px !default;
27 | $select-padding-horizontal: 10px !default;
28 |
29 | // menu options
30 | $select-menu-zindex: 1000 !default;
31 | $select-menu-max-height: 200px !default;
32 | $select-menu-box-shadow: $select-input-hover-box-shadow !default;
33 |
34 | $select-option-color: lighten($select-text-color, 20%) !default;
35 | $select-option-bg: $select-input-bg !default;
36 | $select-option-focused-color: $select-text-color !default;
37 | $select-option-focused-bg: #f2f9fc !default; // pale blue
38 | $select-option-selected-color: $select-text-color !default;
39 | $select-option-selected-bg: #f5faff !default; // lightest blue
40 | $select-option-disabled-color: lighten($select-text-color, 60%) !default;
41 |
42 | $select-noresults-color: lighten($select-text-color, 40%) !default;
43 |
44 | // clear "x" button
45 | $select-clear-size: floor(($select-input-height / 2)) !default;
46 | $select-clear-color: #999 !default;
47 | $select-clear-hover-color: #D0021B !default; // red
48 | $select-clear-width: ($select-input-internal-height / 2) !default;
49 |
50 | // arrow indicator
51 | $select-arrow-color: #999 !default;
52 | $select-arrow-color-hover: #666 !default;
53 | $select-arrow-width: 5px !default;
54 |
55 | // loading indicator
56 | $select-loading-size: 16px !default;
57 | $select-loading-color: $select-text-color !default;
58 | $select-loading-color-bg: $select-input-border-color !default;
59 |
60 | // multi-select item
61 | $select-item-border-radius: 2px !default;
62 | $select-item-gutter: 5px !default;
63 | $select-item-padding-vertical: 2px !default;
64 | $select-item-padding-horizontal: 5px !default;
65 | $select-item-font-size: .9em !default;
66 | $select-item-color: #08c !default; // pale blue
67 | $select-item-bg: #f2f9fc !default;
68 | $select-item-border-color: darken($select-item-bg, 10%) !default;
69 | $select-item-hover-color: darken($select-item-color, 5%) !default; // pale blue
70 | $select-item-hover-bg: darken($select-item-bg, 5%) !default;
71 | $select-item-disabled-color: #333 !default;
72 | $select-item-disabled-bg: #fcfcfc !default;
73 | $select-item-disabled-border-color: darken($select-item-disabled-bg, 10%) !default;
74 |
--------------------------------------------------------------------------------
/scss/spinner.scss:
--------------------------------------------------------------------------------
1 | //
2 | // Spinner
3 | // ------------------------------
4 | @import 'mixins';
5 |
6 | @mixin Select-spinner($size, $orbit, $satellite)
7 | {
8 | @include animation( Select-animation-spin 400ms infinite linear );
9 | @include square($size);
10 | box-sizing: border-box;
11 | border-radius: 50%;
12 | border: floor(($size / 8)) solid $orbit;
13 | border-right-color: $satellite;
14 | display: inline-block;
15 | position: relative;
16 |
17 | }
18 |
19 | @keyframes Select-animation-spin {
20 | to { transform: rotate(1turn); }
21 | }
22 | @-webkit-keyframes Select-animation-spin {
23 | to { -webkit-transform: rotate(1turn); }
24 | }
25 |
--------------------------------------------------------------------------------
/src/Async.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 | import React, { Component } from 'react';
3 | import Select from './Select';
4 |
5 | import stripDiacritics from './utils/stripDiacritics';
6 |
7 | const propTypes = {
8 | autoload: PropTypes.bool.isRequired, // automatically call the `loadOptions` prop on-mount; defaults to true
9 | cache: PropTypes.any, // object to use to cache results; set to null/false to disable caching
10 | children: PropTypes.func.isRequired, // Child function responsible for creating the inner Select component; (props: Object): PropTypes.element
11 | ignoreAccents: PropTypes.bool, // strip diacritics when filtering; defaults to true
12 | ignoreCase: PropTypes.bool, // perform case-insensitive filtering; defaults to true
13 | loadOptions: PropTypes.func.isRequired, // callback to load options asynchronously; (inputValue: string, callback: Function): ?Promise
14 | loadingPlaceholder: PropTypes.oneOfType([ // replaces the placeholder while options are loading
15 | PropTypes.string,
16 | PropTypes.node
17 | ]),
18 | multi: PropTypes.bool, // multi-value input
19 | noResultsText: PropTypes.oneOfType([ // field noResultsText, displayed when no options come back from the server
20 | PropTypes.string,
21 | PropTypes.node
22 | ]),
23 | onChange: PropTypes.func, // onChange handler: function (newValue) {}
24 | onInputChange: PropTypes.func, // optional for keeping track of what is being typed
25 | options: PropTypes.array.isRequired, // array of options
26 | placeholder: PropTypes.oneOfType([ // field placeholder, displayed when there's no value (shared with Select)
27 | PropTypes.string,
28 | PropTypes.node
29 | ]),
30 | searchPromptText: PropTypes.oneOfType([ // label to prompt for search input
31 | PropTypes.string,
32 | PropTypes.node
33 | ]),
34 | value: PropTypes.any, // initial field value
35 | };
36 |
37 | const defaultCache = {};
38 |
39 | const defaultChildren = props => ;
40 |
41 | const defaultProps = {
42 | autoload: true,
43 | cache: defaultCache,
44 | children: defaultChildren,
45 | ignoreAccents: true,
46 | ignoreCase: true,
47 | loadingPlaceholder: 'Loading...',
48 | options: [],
49 | searchPromptText: 'Type to search',
50 | };
51 |
52 | export default class Async extends Component {
53 | constructor (props, context) {
54 | super(props, context);
55 |
56 | this._cache = props.cache === defaultCache ? {} : props.cache;
57 |
58 | this.state = {
59 | inputValue: '',
60 | isLoading: false,
61 | options: props.options,
62 | };
63 |
64 | this.onInputChange = this.onInputChange.bind(this);
65 | }
66 |
67 | componentDidMount () {
68 | const { autoload } = this.props;
69 |
70 | if (autoload) {
71 | this.loadOptions('');
72 | }
73 | }
74 |
75 | componentWillReceiveProps(nextProps) {
76 | if (nextProps.options !== this.props.options) {
77 | this.setState({
78 | options: nextProps.options,
79 | });
80 | }
81 | }
82 |
83 | componentWillUnmount () {
84 | this._callback = null;
85 | }
86 |
87 | loadOptions (inputValue) {
88 | const { loadOptions } = this.props;
89 | const cache = this._cache;
90 |
91 | if (
92 | cache &&
93 | Object.prototype.hasOwnProperty.call(cache, inputValue)
94 | ) {
95 | this._callback = null;
96 |
97 | this.setState({
98 | isLoading: false,
99 | options: cache[inputValue]
100 | });
101 |
102 | return;
103 | }
104 |
105 | const callback = (error, data) => {
106 | const options = data && data.options || [];
107 |
108 | if (cache) {
109 | cache[inputValue] = options;
110 | }
111 |
112 | if (callback === this._callback) {
113 | this._callback = null;
114 |
115 | this.setState({
116 | isLoading: false,
117 | options
118 | });
119 | }
120 | };
121 |
122 | // Ignore all but the most recent request
123 | this._callback = callback;
124 |
125 | const promise = loadOptions(inputValue, callback);
126 | if (promise) {
127 | promise.then(
128 | (data) => callback(null, data),
129 | (error) => callback(error)
130 | );
131 | }
132 |
133 | if (
134 | this._callback &&
135 | !this.state.isLoading
136 | ) {
137 | this.setState({
138 | isLoading: true
139 | });
140 | }
141 | }
142 |
143 | onInputChange (inputValue) {
144 | const { ignoreAccents, ignoreCase, onInputChange } = this.props;
145 | let newInputValue = inputValue;
146 |
147 | if (onInputChange) {
148 | const value = onInputChange(newInputValue);
149 | // Note: != used deliberately here to catch undefined and null
150 | if (value != null && typeof value !== 'object') {
151 | newInputValue = '' + value;
152 | }
153 | }
154 |
155 | let transformedInputValue = newInputValue;
156 |
157 | if (ignoreAccents) {
158 | transformedInputValue = stripDiacritics(transformedInputValue);
159 | }
160 |
161 | if (ignoreCase) {
162 | transformedInputValue = transformedInputValue.toLowerCase();
163 | }
164 |
165 | this.setState({ inputValue: newInputValue });
166 | this.loadOptions(transformedInputValue);
167 |
168 | // Return new input value, but without applying toLowerCase() to avoid modifying the user's view case of the input while typing.
169 | return newInputValue;
170 | }
171 |
172 | noResultsText() {
173 | const { loadingPlaceholder, noResultsText, searchPromptText } = this.props;
174 | const { inputValue, isLoading } = this.state;
175 |
176 | if (isLoading) {
177 | return loadingPlaceholder;
178 | }
179 | if (inputValue && noResultsText) {
180 | return noResultsText;
181 | }
182 | return searchPromptText;
183 | }
184 |
185 | focus () {
186 | this.select.focus();
187 | }
188 |
189 | render () {
190 | const { children, loadingPlaceholder, placeholder } = this.props;
191 | const { isLoading, options } = this.state;
192 |
193 | const props = {
194 | noResultsText: this.noResultsText(),
195 | placeholder: isLoading ? loadingPlaceholder : placeholder,
196 | options: (isLoading && loadingPlaceholder) ? [] : options,
197 | ref: (ref) => (this.select = ref),
198 | };
199 |
200 | return children({
201 | ...this.props,
202 | ...props,
203 | isLoading,
204 | onInputChange: this.onInputChange
205 | });
206 | }
207 | }
208 |
209 | Async.propTypes = propTypes;
210 | Async.defaultProps = defaultProps;
211 |
--------------------------------------------------------------------------------
/src/AsyncCreatable.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 | import React from 'react';
3 |
4 | import Async from './Async';
5 | import Creatable from './Creatable';
6 | import Select from './Select';
7 |
8 | class AsyncCreatableSelect extends React.Component {
9 |
10 | focus () {
11 | this.select.focus();
12 | }
13 |
14 | render () {
15 | return (
16 |
17 | {({ ref, ...asyncProps }) => {
18 | const asyncRef = ref;
19 | return (
20 | {({ ref, ...creatableProps }) => {
21 | const creatableRef = ref;
22 | return this.props.children({
23 | ...creatableProps,
24 | ref: (select) => {
25 | creatableRef(select);
26 | asyncRef(select);
27 | this.select = select;
28 | }
29 | });
30 | }}
31 | );
32 | }}
33 |
34 | );
35 | }
36 | }
37 |
38 | const defaultChildren = props => ;
39 |
40 | AsyncCreatableSelect.propTypes = {
41 | children: PropTypes.func.isRequired, // Child function responsible for creating the inner Select component; (props: Object): PropTypes.element
42 | };
43 |
44 | AsyncCreatableSelect.defaultProps = {
45 | children: defaultChildren,
46 | };
47 |
48 | export default AsyncCreatableSelect;
49 |
--------------------------------------------------------------------------------
/src/Creatable.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types';
2 | import React from 'react';
3 |
4 | import defaultFilterOptions from './utils/defaultFilterOptions';
5 | import defaultMenuRenderer from './utils/defaultMenuRenderer';
6 | import Select from './Select';
7 |
8 | class CreatableSelect extends React.Component {
9 | constructor (props, context) {
10 | super(props, context);
11 |
12 | this.filterOptions = this.filterOptions.bind(this);
13 | this.menuRenderer = this.menuRenderer.bind(this);
14 | this.onInputKeyDown = this.onInputKeyDown.bind(this);
15 | this.onInputChange = this.onInputChange.bind(this);
16 | this.onOptionSelect = this.onOptionSelect.bind(this);
17 | }
18 |
19 | createNewOption () {
20 | const {
21 | isValidNewOption,
22 | newOptionCreator,
23 | onNewOptionClick,
24 | options = [],
25 | } = this.props;
26 |
27 | if (isValidNewOption({ label: this.inputValue })) {
28 | const option = newOptionCreator({ label: this.inputValue, labelKey: this.labelKey, valueKey: this.valueKey });
29 | const isOptionUnique = this.isOptionUnique({ option, options });
30 |
31 | // Don't add the same option twice.
32 | if (isOptionUnique) {
33 | if (onNewOptionClick) {
34 | onNewOptionClick(option);
35 | } else {
36 | options.unshift(option);
37 |
38 | this.select.selectValue(option);
39 | }
40 | }
41 | }
42 | }
43 |
44 | filterOptions (...params) {
45 | const { filterOptions, isValidNewOption, promptTextCreator } = this.props;
46 |
47 | // TRICKY Check currently selected options as well.
48 | // Don't display a create-prompt for a value that's selected.
49 | // This covers async edge-cases where a newly-created Option isn't yet in the async-loaded array.
50 | const excludeOptions = params[2] || [];
51 |
52 | const filteredOptions = filterOptions(...params) || [];
53 |
54 | if (isValidNewOption({ label: this.inputValue })) {
55 | const { newOptionCreator } = this.props;
56 |
57 | const option = newOptionCreator({
58 | label: this.inputValue,
59 | labelKey: this.labelKey,
60 | valueKey: this.valueKey
61 | });
62 |
63 | // TRICKY Compare to all options (not just filtered options) in case option has already been selected).
64 | // For multi-selects, this would remove it from the filtered list.
65 | const isOptionUnique = this.isOptionUnique({
66 | option,
67 | options: excludeOptions.concat(filteredOptions)
68 | });
69 |
70 | if (isOptionUnique) {
71 | const prompt = promptTextCreator(this.inputValue);
72 |
73 | this._createPlaceholderOption = newOptionCreator({
74 | label: prompt,
75 | labelKey: this.labelKey,
76 | valueKey: this.valueKey
77 | });
78 |
79 | filteredOptions.unshift(this._createPlaceholderOption);
80 | }
81 | }
82 |
83 | return filteredOptions;
84 | }
85 |
86 | isOptionUnique ({
87 | option,
88 | options
89 | }) {
90 | const { isOptionUnique } = this.props;
91 |
92 | options = options || this.select.filterFlatOptions();
93 |
94 | return isOptionUnique({
95 | labelKey: this.labelKey,
96 | option,
97 | options,
98 | valueKey: this.valueKey
99 | });
100 | }
101 |
102 | menuRenderer (params) {
103 | const { menuRenderer } = this.props;
104 |
105 | return menuRenderer({
106 | ...params,
107 | onSelect: this.onOptionSelect,
108 | selectValue: this.onOptionSelect
109 | });
110 | }
111 |
112 | onInputChange (input) {
113 | const { onInputChange } = this.props;
114 |
115 | // This value may be needed in between Select mounts (when this.select is null)
116 | this.inputValue = input;
117 |
118 | if (onInputChange) {
119 | this.inputValue = onInputChange(input);
120 | }
121 |
122 | return this.inputValue;
123 | }
124 |
125 | onInputKeyDown (event) {
126 | const { shouldKeyDownEventCreateNewOption, onInputKeyDown } = this.props;
127 | const focusedOption = this.select.getFocusedOption();
128 |
129 | if (
130 | focusedOption &&
131 | focusedOption === this._createPlaceholderOption &&
132 | shouldKeyDownEventCreateNewOption({ keyCode: event.keyCode })
133 | ) {
134 | this.createNewOption();
135 |
136 | // Prevent decorated Select from doing anything additional with this keyDown event
137 | event.preventDefault();
138 | } else if (onInputKeyDown) {
139 | onInputKeyDown(event);
140 | }
141 | }
142 |
143 | onOptionSelect (option) {
144 | if (option === this._createPlaceholderOption) {
145 | this.createNewOption();
146 | } else {
147 | this.select.selectValue(option);
148 | }
149 | }
150 |
151 | focus () {
152 | this.select.focus();
153 | }
154 |
155 | render () {
156 | const {
157 | ref: refProp,
158 | ...restProps
159 | } = this.props;
160 |
161 | let { children } = this.props;
162 |
163 | // We can't use destructuring default values to set the children,
164 | // because it won't apply work if `children` is null. A falsy check is
165 | // more reliable in real world use-cases.
166 | if (!children) {
167 | children = defaultChildren;
168 | }
169 |
170 | const props = {
171 | ...restProps,
172 | allowCreate: true,
173 | filterOptions: this.filterOptions,
174 | menuRenderer: this.menuRenderer,
175 | onInputChange: this.onInputChange,
176 | onInputKeyDown: this.onInputKeyDown,
177 | ref: (ref) => {
178 | this.select = ref;
179 |
180 | // These values may be needed in between Select mounts (when this.select is null)
181 | if (ref) {
182 | this.labelKey = ref.props.labelKey;
183 | this.valueKey = ref.props.valueKey;
184 | }
185 | if (refProp) {
186 | refProp(ref);
187 | }
188 | }
189 | };
190 |
191 | return children(props);
192 | }
193 | }
194 |
195 | const defaultChildren = props => ;
196 |
197 | const isOptionUnique = ({ option, options, labelKey, valueKey }) => {
198 | if (!options || !options.length) {
199 | return true;
200 | }
201 |
202 | return options
203 | .filter((existingOption) =>
204 | existingOption[labelKey] === option[labelKey] ||
205 | existingOption[valueKey] === option[valueKey]
206 | )
207 | .length === 0;
208 | };
209 |
210 | const isValidNewOption = ({ label }) => !!label;
211 |
212 | const newOptionCreator = ({ label, labelKey, valueKey }) => {
213 | const option = {};
214 | option[valueKey] = label;
215 | option[labelKey] = label;
216 | option.className = 'Select-create-option-placeholder';
217 |
218 | return option;
219 | };
220 |
221 | const promptTextCreator = label => `Create option "${label}"`;
222 |
223 | const shouldKeyDownEventCreateNewOption = ({ keyCode }) => {
224 | switch (keyCode) {
225 | case 9: // TAB
226 | case 13: // ENTER
227 | case 188: // COMMA
228 | return true;
229 | default:
230 | return false;
231 | }
232 | };
233 |
234 | // Default prop methods
235 | CreatableSelect.isOptionUnique = isOptionUnique;
236 | CreatableSelect.isValidNewOption = isValidNewOption;
237 | CreatableSelect.newOptionCreator = newOptionCreator;
238 | CreatableSelect.promptTextCreator = promptTextCreator;
239 | CreatableSelect.shouldKeyDownEventCreateNewOption = shouldKeyDownEventCreateNewOption;
240 |
241 |
242 | CreatableSelect.defaultProps = {
243 | filterOptions: defaultFilterOptions,
244 | isOptionUnique,
245 | isValidNewOption,
246 | menuRenderer: defaultMenuRenderer,
247 | newOptionCreator,
248 | promptTextCreator,
249 | shouldKeyDownEventCreateNewOption
250 | };
251 |
252 | CreatableSelect.propTypes = {
253 | // Child function responsible for creating the inner Select component
254 | // This component can be used to compose HOCs (eg Creatable and Async)
255 | // (props: Object): PropTypes.element
256 | children: PropTypes.func,
257 |
258 | // See Select.propTypes.filterOptions
259 | filterOptions: PropTypes.any,
260 |
261 | // Searches for any matching option within the set of options.
262 | // This function prevents duplicate options from being created.
263 | // ({ option: Object, options: Array, labelKey: string, valueKey: string }): boolean
264 | isOptionUnique: PropTypes.func,
265 |
266 | // Determines if the current input text represents a valid option.
267 | // ({ label: string }): boolean
268 | isValidNewOption: PropTypes.func,
269 |
270 | // See Select.propTypes.menuRenderer
271 | menuRenderer: PropTypes.any,
272 |
273 | // Factory to create new option.
274 | // ({ label: string, labelKey: string, valueKey: string }): Object
275 | newOptionCreator: PropTypes.func,
276 |
277 | // input change handler: function (inputValue) {}
278 | onInputChange: PropTypes.func,
279 |
280 | // input keyDown handler: function (event) {}
281 | onInputKeyDown: PropTypes.func,
282 |
283 | // new option click handler: function (option) {}
284 | onNewOptionClick: PropTypes.func,
285 |
286 | // See Select.propTypes.options
287 | options: PropTypes.array,
288 |
289 | // Creates prompt/placeholder option text.
290 | // (filterText: string): string
291 | promptTextCreator: PropTypes.func,
292 |
293 | ref: PropTypes.func,
294 |
295 | // Decides if a keyDown event (eg its `keyCode`) should result in the creation of a new option.
296 | shouldKeyDownEventCreateNewOption: PropTypes.func,
297 | };
298 |
299 | export default CreatableSelect;
300 |
--------------------------------------------------------------------------------
/src/Dropdown.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 |
4 | class Dropdown extends React.Component {
5 | render () {
6 | // This component adds no markup
7 | return this.props.children;
8 | }
9 | };
10 |
11 | Dropdown.propTypes = {
12 | children: PropTypes.node,
13 | };
14 |
15 | export default Dropdown;
16 |
--------------------------------------------------------------------------------
/src/Option.js:
--------------------------------------------------------------------------------
1 | import classNames from 'classnames';
2 | import PropTypes from 'prop-types';
3 | import React from 'react';
4 | import blockEvent from './utils/blockEvent';
5 |
6 | class Option extends React.Component {
7 |
8 | constructor(props) {
9 | super(props);
10 |
11 | this.handleMouseDown = this.handleMouseDown.bind(this);
12 | this.handleMouseEnter = this.handleMouseEnter.bind(this);
13 | this.handleMouseMove = this.handleMouseMove.bind(this);
14 | this.handleTouchStart = this.handleTouchStart.bind(this);
15 | this.handleTouchEnd = this.handleTouchEnd.bind(this);
16 | this.handleTouchMove = this.handleTouchMove.bind(this);
17 | this.onFocus = this.onFocus.bind(this);
18 | }
19 |
20 | handleMouseDown (event) {
21 | event.preventDefault();
22 | event.stopPropagation();
23 | this.props.onSelect(this.props.option, event);
24 | }
25 |
26 | handleMouseEnter (event) {
27 | this.onFocus(event);
28 | }
29 |
30 | handleMouseMove (event) {
31 | this.onFocus(event);
32 | }
33 |
34 | handleTouchEnd(event){
35 | // Check if the view is being dragged, In this case
36 | // we don't want to fire the click event (because the user only wants to scroll)
37 | if(this.dragging) return;
38 |
39 | this.handleMouseDown(event);
40 | }
41 |
42 | handleTouchMove () {
43 | // Set a flag that the view is being dragged
44 | this.dragging = true;
45 | }
46 |
47 | handleTouchStart () {
48 | // Set a flag that the view is not being dragged
49 | this.dragging = false;
50 | }
51 |
52 | onFocus (event) {
53 | if (!this.props.isFocused) {
54 | this.props.onFocus(this.props.option, event);
55 | }
56 | }
57 |
58 | render () {
59 | const { option, instancePrefix, optionIndex } = this.props;
60 | const className = classNames(this.props.className, option.className);
61 |
62 | return option.disabled ? (
63 |
66 | {this.props.children}
67 |
68 | ) : (
69 |
81 | {this.props.children}
82 |
83 | );
84 | }
85 | }
86 |
87 | Option.propTypes = {
88 | children: PropTypes.node,
89 | className: PropTypes.string, // className (based on mouse position)
90 | instancePrefix: PropTypes.string.isRequired, // unique prefix for the ids (used for aria)
91 | isDisabled: PropTypes.bool, // the option is disabled
92 | isFocused: PropTypes.bool, // the option is focused
93 | isSelected: PropTypes.bool, // the option is selected
94 | onFocus: PropTypes.func, // method to handle mouseEnter on option element
95 | onSelect: PropTypes.func, // method to handle click on option element
96 | onUnfocus: PropTypes.func, // method to handle mouseLeave on option element
97 | option: PropTypes.object.isRequired, // object that is base for that option
98 | optionIndex: PropTypes.number, // index of the option, used to generate unique ids for aria
99 | };
100 |
101 | export default Option;
102 |
--------------------------------------------------------------------------------
/src/OptionGroup.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import classNames from 'classnames';
4 |
5 | class OptionGroup extends React.Component {
6 |
7 | constructor(props) {
8 | super(props);
9 |
10 | this.handleMouseDown = this.handleMouseDown.bind(this);
11 | this.handleTouchEnd = this.handleTouchEnd.bind(this);
12 | this.handleTouchMove = this.handleTouchMove.bind(this);
13 | this.handleTouchStart = this.handleTouchStart.bind(this);
14 | }
15 |
16 | blockEvent (event) {
17 | event.preventDefault();
18 | event.stopPropagation();
19 | if ((event.target.tagName !== 'A') || !('href' in event.target)) {
20 | return;
21 | }
22 | if (event.target.target) {
23 | window.open(event.target.href, event.target.target);
24 | } else {
25 | window.location.href = event.target.href;
26 | }
27 | }
28 |
29 | handleMouseDown (event) {
30 | event.preventDefault();
31 | event.stopPropagation();
32 | }
33 |
34 | handleTouchEnd(event){
35 | // Check if the view is being dragged, In this case
36 | // we don't want to fire the click event (because the user only wants to scroll)
37 | if(this.dragging) return;
38 |
39 | this.handleMouseDown(event);
40 | }
41 |
42 | handleTouchMove (event) {
43 | // Set a flag that the view is being dragged
44 | this.dragging = true;
45 | }
46 |
47 | handleTouchStart (event) {
48 | // Set a flag that the view is not being dragged
49 | this.dragging = false;
50 | }
51 |
52 | render () {
53 | var { option } = this.props;
54 | var className = classNames(this.props.className, option.className);
55 |
56 | return option.disabled ? (
57 |
60 | {this.props.children}
61 |
62 | ) : (
63 |
72 |
73 | {this.props.label}
74 |
75 | {this.props.children}
76 |
77 | );
78 | }
79 | };
80 |
81 | OptionGroup.propTypes = {
82 | children: PropTypes.any,
83 | className: PropTypes.string, // className (based on mouse position)
84 | label: PropTypes.node, // the heading to show above the child options
85 | option: PropTypes.object.isRequired, // object that is base for that option group
86 | };
87 |
88 | export default OptionGroup;
89 |
--------------------------------------------------------------------------------
/src/Value.js:
--------------------------------------------------------------------------------
1 | import classNames from 'classnames';
2 | import PropTypes from 'prop-types';
3 | import React from 'react';
4 |
5 | class Value extends React.Component {
6 |
7 | constructor(props) {
8 | super(props);
9 |
10 | this.handleMouseDown = this.handleMouseDown.bind(this);
11 | this.onRemove = this.onRemove.bind(this);
12 | this.handleTouchEndRemove = this.handleTouchEndRemove.bind(this);
13 | this.handleTouchMove = this.handleTouchMove.bind(this);
14 | this.handleTouchStart = this.handleTouchStart.bind(this);
15 | }
16 |
17 | handleMouseDown (event) {
18 | if (event.type === 'mousedown' && event.button !== 0) {
19 | return;
20 | }
21 | if (this.props.onClick) {
22 | event.stopPropagation();
23 | this.props.onClick(this.props.value, event);
24 | return;
25 | }
26 | if (this.props.value.href) {
27 | event.stopPropagation();
28 | }
29 | }
30 |
31 | onRemove (event) {
32 | event.preventDefault();
33 | event.stopPropagation();
34 | this.props.onRemove(this.props.value);
35 | }
36 |
37 | handleTouchEndRemove (event){
38 | // Check if the view is being dragged, In this case
39 | // we don't want to fire the click event (because the user only wants to scroll)
40 | if(this.dragging) return;
41 |
42 | // Fire the mouse events
43 | this.onRemove(event);
44 | }
45 |
46 | handleTouchMove () {
47 | // Set a flag that the view is being dragged
48 | this.dragging = true;
49 | }
50 |
51 | handleTouchStart () {
52 | // Set a flag that the view is not being dragged
53 | this.dragging = false;
54 | }
55 |
56 | renderRemoveIcon () {
57 | if (this.props.disabled || !this.props.onRemove) return;
58 | return (
59 |
65 | ×
66 |
67 | );
68 | }
69 |
70 | renderLabel () {
71 | let className = 'Select-value-label';
72 | return this.props.onClick || this.props.value.href ? (
73 |
74 | {this.props.children}
75 |
76 | ) : (
77 |
78 | {this.props.children}
79 |
80 | );
81 | }
82 |
83 | render () {
84 | return (
85 |
89 | {this.renderRemoveIcon()}
90 | {this.renderLabel()}
91 |
92 | );
93 | }
94 | }
95 |
96 | Value.propTypes = {
97 | children: PropTypes.node,
98 | disabled: PropTypes.bool, // disabled prop passed to ReactSelect
99 | id: PropTypes.string, // Unique id for the value - used for aria
100 | onClick: PropTypes.func, // method to handle click on value label
101 | onRemove: PropTypes.func, // method to handle removal of the value
102 | value: PropTypes.object.isRequired, // the option object for this value
103 | };
104 |
105 | export default Value;
106 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import Select from './Select';
2 | import Async from './Async';
3 | import AsyncCreatable from './AsyncCreatable';
4 | import Creatable from './Creatable';
5 | import Value from './Value';
6 | import Option from './Option';
7 |
8 | import defaultMenuRenderer from './utils/defaultMenuRenderer';
9 | import defaultArrowRenderer from './utils/defaultArrowRenderer';
10 | import defaultClearRenderer from './utils/defaultClearRenderer';
11 | import defaultFilterOptions from './utils/defaultFilterOptions';
12 |
13 | Select.Async = Async;
14 | Select.AsyncCreatable = AsyncCreatable;
15 | Select.Creatable = Creatable;
16 | Select.Value = Value;
17 | Select.Option = Option;
18 |
19 | export default Select;
20 | export {
21 | Async,
22 | AsyncCreatable,
23 | Creatable,
24 | Value,
25 | Option,
26 | defaultMenuRenderer,
27 | defaultArrowRenderer,
28 | defaultClearRenderer,
29 | defaultFilterOptions
30 | };
31 |
--------------------------------------------------------------------------------
/src/index.umd.js:
--------------------------------------------------------------------------------
1 | // This file exists as an entry point for bundling our umd builds.
2 | // Both in rollup and in webpack, umd builds built from es6 modules are not
3 | // compatible with mixed imports (which exist in index.js)
4 | // This file does away with named imports in favor of a single export default.
5 |
6 | import Select from './Select';
7 | import Async from './Async';
8 | import AsyncCreatable from './AsyncCreatable';
9 | import Creatable from './Creatable';
10 | import Value from './Value';
11 | import Option from './Option';
12 |
13 | Select.Async = Async;
14 | Select.AsyncCreatable = AsyncCreatable;
15 | Select.Creatable = Creatable;
16 | Select.Value = Value;
17 | Select.Option = Option;
18 |
19 | export default Select;
20 |
--------------------------------------------------------------------------------
/src/utils/blockEvent.js:
--------------------------------------------------------------------------------
1 | export default event => {
2 | event.preventDefault();
3 | event.stopPropagation();
4 | if ((event.target.tagName !== 'A') || !('href' in event.target)) {
5 | return;
6 | }
7 | if (event.target.target) {
8 | window.open(event.target.href, event.target.target);
9 | } else {
10 | window.location.href = event.target.href;
11 | }
12 | };
13 |
--------------------------------------------------------------------------------
/src/utils/defaultArrowRenderer.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 |
4 | const arrowRenderer = ({ onMouseDown }) => {
5 | return (
6 |
10 | );
11 | };
12 |
13 | arrowRenderer.propTypes = {
14 | onMouseDown: PropTypes.func,
15 | };
16 |
17 | export default arrowRenderer;
18 |
--------------------------------------------------------------------------------
/src/utils/defaultClearRenderer.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const clearRenderer = () => {
4 | return (
5 |
9 | );
10 | };
11 |
12 | export default clearRenderer;
13 |
--------------------------------------------------------------------------------
/src/utils/defaultFilterOptions.js:
--------------------------------------------------------------------------------
1 | import stripDiacritics from './stripDiacritics';
2 | import trim from './trim';
3 |
4 | const isValid = value => {
5 | return typeof (value) !== 'undefined' && value !== null && value !== '';
6 | };
7 |
8 | const filterOptions = (options, filterValue, excludeOptions, props) => {
9 | if (props.ignoreAccents) {
10 | filterValue = stripDiacritics(filterValue);
11 | }
12 |
13 | if (props.ignoreCase) {
14 | filterValue = filterValue.toLowerCase();
15 | }
16 |
17 | if (props.trimFilter) {
18 | filterValue = trim(filterValue);
19 | }
20 |
21 | if (excludeOptions) excludeOptions = excludeOptions.map(i => i[props.valueKey]);
22 |
23 | return options.filter(option => {
24 | if (excludeOptions && excludeOptions.indexOf(option[props.valueKey]) > -1) return false;
25 | if (props.filterOption) return props.filterOption.call(undefined, option, filterValue);
26 | if (!filterValue) return true;
27 |
28 | const value = option[props.valueKey];
29 | const label = option[props.labelKey];
30 | const hasValue = isValid(value);
31 | const hasLabel = isValid(label);
32 |
33 | if (!hasValue && !hasLabel) {
34 | return false;
35 | }
36 |
37 | let valueTest = hasValue ? String(value) : null;
38 | let labelTest = hasLabel ? String(label) : null;
39 |
40 | if (props.ignoreAccents) {
41 | if (valueTest && props.matchProp !== 'label') valueTest = stripDiacritics(valueTest);
42 | if (labelTest && props.matchProp !== 'value') labelTest = stripDiacritics(labelTest);
43 | }
44 |
45 | if (props.ignoreCase) {
46 | if (valueTest && props.matchProp !== 'label') valueTest = valueTest.toLowerCase();
47 | if (labelTest && props.matchProp !== 'value') labelTest = labelTest.toLowerCase();
48 | }
49 |
50 | return props.matchPos === 'start' ? (
51 | (valueTest && props.matchProp !== 'label' && valueTest.substr(0, filterValue.length) === filterValue) ||
52 | (labelTest && props.matchProp !== 'value' && labelTest.substr(0, filterValue.length) === filterValue)
53 | ) : (
54 | (valueTest && props.matchProp !== 'label' && valueTest.indexOf(filterValue) >= 0) ||
55 | (labelTest && props.matchProp !== 'value' && labelTest.indexOf(filterValue) >= 0)
56 | );
57 | });
58 | };
59 |
60 | export default filterOptions;
61 |
--------------------------------------------------------------------------------
/src/utils/defaultMenuRenderer.js:
--------------------------------------------------------------------------------
1 | import classNames from 'classnames';
2 | import PropTypes from 'prop-types';
3 | import React from 'react';
4 |
5 | const isGroup = (option) => {
6 | return option && Array.isArray(option.options);
7 | };
8 |
9 | const menuRenderer = ({
10 | focusedOption,
11 | focusOption,
12 | inputValue,
13 | instancePrefix,
14 | onFocus,
15 | onOptionRef,
16 | onSelect,
17 | optionClassName,
18 | optionComponent,
19 | optionGroupComponent,
20 | optionRenderer,
21 | options,
22 | removeValue,
23 | selectValue,
24 | valueArray,
25 | valueKey,
26 | }) => {
27 | let OptionGroup = optionGroupComponent;
28 | let Option = optionComponent;
29 | let renderLabel = optionRenderer;
30 |
31 | const renderOptions = (optionsSubset) => {
32 | return optionsSubset.map((option, i) => {
33 | if (isGroup(option)) {
34 | let optionGroupClass = classNames({
35 | 'Select-option-group': true,
36 | });
37 |
38 | return (
39 |
46 | {renderOptions(option.options)}
47 |
48 | );
49 | } else {
50 | let isSelected = valueArray && valueArray.indexOf(option) > -1;
51 | let isFocused = option === focusedOption;
52 | let optionRef = isFocused ? 'focused' : null;
53 | let optionClass = classNames(optionClassName, {
54 | 'Select-option': true,
55 | 'is-selected': isSelected,
56 | 'is-focused': isFocused,
57 | 'is-disabled': option.disabled,
58 | });
59 |
60 | return (
61 | { onOptionRef(ref, isFocused); }}
75 | removeValue={removeValue}
76 | selectValue={selectValue}
77 | >
78 | {renderLabel(option, i)}
79 |
80 | );
81 | }
82 | });
83 | };
84 |
85 | return renderOptions(options);
86 | };
87 |
88 | menuRenderer.propTypes = {
89 | focusOption: PropTypes.func,
90 | focusedOption: PropTypes.object,
91 | inputValue: PropTypes.string,
92 | instancePrefix: PropTypes.string,
93 | onFocus: PropTypes.func,
94 | onOptionRef: PropTypes.func,
95 | onSelect: PropTypes.func,
96 | optionClassName: PropTypes.string,
97 | optionComponent: PropTypes.func,
98 | optionRenderer: PropTypes.func,
99 | options: PropTypes.array,
100 | removeValue: PropTypes.func,
101 | selectValue: PropTypes.func,
102 | valueArray: PropTypes.array,
103 | valueKey: PropTypes.string,
104 | };
105 |
106 | export default menuRenderer;
107 |
--------------------------------------------------------------------------------
/src/utils/stripDiacritics.js:
--------------------------------------------------------------------------------
1 | const map = [
2 | { 'base':'A', 'letters':/[\u0041\u24B6\uFF21\u00C0\u00C1\u00C2\u1EA6\u1EA4\u1EAA\u1EA8\u00C3\u0100\u0102\u1EB0\u1EAE\u1EB4\u1EB2\u0226\u01E0\u00C4\u01DE\u1EA2\u00C5\u01FA\u01CD\u0200\u0202\u1EA0\u1EAC\u1EB6\u1E00\u0104\u023A\u2C6F]/g },
3 | { 'base':'AA','letters':/[\uA732]/g },
4 | { 'base':'AE','letters':/[\u00C6\u01FC\u01E2]/g },
5 | { 'base':'AO','letters':/[\uA734]/g },
6 | { 'base':'AU','letters':/[\uA736]/g },
7 | { 'base':'AV','letters':/[\uA738\uA73A]/g },
8 | { 'base':'AY','letters':/[\uA73C]/g },
9 | { 'base':'B', 'letters':/[\u0042\u24B7\uFF22\u1E02\u1E04\u1E06\u0243\u0182\u0181]/g },
10 | { 'base':'C', 'letters':/[\u0043\u24B8\uFF23\u0106\u0108\u010A\u010C\u00C7\u1E08\u0187\u023B\uA73E]/g },
11 | { 'base':'D', 'letters':/[\u0044\u24B9\uFF24\u1E0A\u010E\u1E0C\u1E10\u1E12\u1E0E\u0110\u018B\u018A\u0189\uA779]/g },
12 | { 'base':'DZ','letters':/[\u01F1\u01C4]/g },
13 | { 'base':'Dz','letters':/[\u01F2\u01C5]/g },
14 | { 'base':'E', 'letters':/[\u0045\u24BA\uFF25\u00C8\u00C9\u00CA\u1EC0\u1EBE\u1EC4\u1EC2\u1EBC\u0112\u1E14\u1E16\u0114\u0116\u00CB\u1EBA\u011A\u0204\u0206\u1EB8\u1EC6\u0228\u1E1C\u0118\u1E18\u1E1A\u0190\u018E]/g },
15 | { 'base':'F', 'letters':/[\u0046\u24BB\uFF26\u1E1E\u0191\uA77B]/g },
16 | { 'base':'G', 'letters':/[\u0047\u24BC\uFF27\u01F4\u011C\u1E20\u011E\u0120\u01E6\u0122\u01E4\u0193\uA7A0\uA77D\uA77E]/g },
17 | { 'base':'H', 'letters':/[\u0048\u24BD\uFF28\u0124\u1E22\u1E26\u021E\u1E24\u1E28\u1E2A\u0126\u2C67\u2C75\uA78D]/g },
18 | { 'base':'I', 'letters':/[\u0049\u24BE\uFF29\u00CC\u00CD\u00CE\u0128\u012A\u012C\u0130\u00CF\u1E2E\u1EC8\u01CF\u0208\u020A\u1ECA\u012E\u1E2C\u0197]/g },
19 | { 'base':'J', 'letters':/[\u004A\u24BF\uFF2A\u0134\u0248]/g },
20 | { 'base':'K', 'letters':/[\u004B\u24C0\uFF2B\u1E30\u01E8\u1E32\u0136\u1E34\u0198\u2C69\uA740\uA742\uA744\uA7A2]/g },
21 | { 'base':'L', 'letters':/[\u004C\u24C1\uFF2C\u013F\u0139\u013D\u1E36\u1E38\u013B\u1E3C\u1E3A\u0141\u023D\u2C62\u2C60\uA748\uA746\uA780]/g },
22 | { 'base':'LJ','letters':/[\u01C7]/g },
23 | { 'base':'Lj','letters':/[\u01C8]/g },
24 | { 'base':'M', 'letters':/[\u004D\u24C2\uFF2D\u1E3E\u1E40\u1E42\u2C6E\u019C]/g },
25 | { 'base':'N', 'letters':/[\u004E\u24C3\uFF2E\u01F8\u0143\u00D1\u1E44\u0147\u1E46\u0145\u1E4A\u1E48\u0220\u019D\uA790\uA7A4]/g },
26 | { 'base':'NJ','letters':/[\u01CA]/g },
27 | { 'base':'Nj','letters':/[\u01CB]/g },
28 | { 'base':'O', 'letters':/[\u004F\u24C4\uFF2F\u00D2\u00D3\u00D4\u1ED2\u1ED0\u1ED6\u1ED4\u00D5\u1E4C\u022C\u1E4E\u014C\u1E50\u1E52\u014E\u022E\u0230\u00D6\u022A\u1ECE\u0150\u01D1\u020C\u020E\u01A0\u1EDC\u1EDA\u1EE0\u1EDE\u1EE2\u1ECC\u1ED8\u01EA\u01EC\u00D8\u01FE\u0186\u019F\uA74A\uA74C]/g },
29 | { 'base':'OI','letters':/[\u01A2]/g },
30 | { 'base':'OO','letters':/[\uA74E]/g },
31 | { 'base':'OU','letters':/[\u0222]/g },
32 | { 'base':'P', 'letters':/[\u0050\u24C5\uFF30\u1E54\u1E56\u01A4\u2C63\uA750\uA752\uA754]/g },
33 | { 'base':'Q', 'letters':/[\u0051\u24C6\uFF31\uA756\uA758\u024A]/g },
34 | { 'base':'R', 'letters':/[\u0052\u24C7\uFF32\u0154\u1E58\u0158\u0210\u0212\u1E5A\u1E5C\u0156\u1E5E\u024C\u2C64\uA75A\uA7A6\uA782]/g },
35 | { 'base':'S', 'letters':/[\u0053\u24C8\uFF33\u1E9E\u015A\u1E64\u015C\u1E60\u0160\u1E66\u1E62\u1E68\u0218\u015E\u2C7E\uA7A8\uA784]/g },
36 | { 'base':'T', 'letters':/[\u0054\u24C9\uFF34\u1E6A\u0164\u1E6C\u021A\u0162\u1E70\u1E6E\u0166\u01AC\u01AE\u023E\uA786]/g },
37 | { 'base':'TZ','letters':/[\uA728]/g },
38 | { 'base':'U', 'letters':/[\u0055\u24CA\uFF35\u00D9\u00DA\u00DB\u0168\u1E78\u016A\u1E7A\u016C\u00DC\u01DB\u01D7\u01D5\u01D9\u1EE6\u016E\u0170\u01D3\u0214\u0216\u01AF\u1EEA\u1EE8\u1EEE\u1EEC\u1EF0\u1EE4\u1E72\u0172\u1E76\u1E74\u0244]/g },
39 | { 'base':'V', 'letters':/[\u0056\u24CB\uFF36\u1E7C\u1E7E\u01B2\uA75E\u0245]/g },
40 | { 'base':'VY','letters':/[\uA760]/g },
41 | { 'base':'W', 'letters':/[\u0057\u24CC\uFF37\u1E80\u1E82\u0174\u1E86\u1E84\u1E88\u2C72]/g },
42 | { 'base':'X', 'letters':/[\u0058\u24CD\uFF38\u1E8A\u1E8C]/g },
43 | { 'base':'Y', 'letters':/[\u0059\u24CE\uFF39\u1EF2\u00DD\u0176\u1EF8\u0232\u1E8E\u0178\u1EF6\u1EF4\u01B3\u024E\u1EFE]/g },
44 | { 'base':'Z', 'letters':/[\u005A\u24CF\uFF3A\u0179\u1E90\u017B\u017D\u1E92\u1E94\u01B5\u0224\u2C7F\u2C6B\uA762]/g },
45 | { 'base':'a', 'letters':/[\u0061\u24D0\uFF41\u1E9A\u00E0\u00E1\u00E2\u1EA7\u1EA5\u1EAB\u1EA9\u00E3\u0101\u0103\u1EB1\u1EAF\u1EB5\u1EB3\u0227\u01E1\u00E4\u01DF\u1EA3\u00E5\u01FB\u01CE\u0201\u0203\u1EA1\u1EAD\u1EB7\u1E01\u0105\u2C65\u0250]/g },
46 | { 'base':'aa','letters':/[\uA733]/g },
47 | { 'base':'ae','letters':/[\u00E6\u01FD\u01E3]/g },
48 | { 'base':'ao','letters':/[\uA735]/g },
49 | { 'base':'au','letters':/[\uA737]/g },
50 | { 'base':'av','letters':/[\uA739\uA73B]/g },
51 | { 'base':'ay','letters':/[\uA73D]/g },
52 | { 'base':'b', 'letters':/[\u0062\u24D1\uFF42\u1E03\u1E05\u1E07\u0180\u0183\u0253]/g },
53 | { 'base':'c', 'letters':/[\u0063\u24D2\uFF43\u0107\u0109\u010B\u010D\u00E7\u1E09\u0188\u023C\uA73F\u2184]/g },
54 | { 'base':'d', 'letters':/[\u0064\u24D3\uFF44\u1E0B\u010F\u1E0D\u1E11\u1E13\u1E0F\u0111\u018C\u0256\u0257\uA77A]/g },
55 | { 'base':'dz','letters':/[\u01F3\u01C6]/g },
56 | { 'base':'e', 'letters':/[\u0065\u24D4\uFF45\u00E8\u00E9\u00EA\u1EC1\u1EBF\u1EC5\u1EC3\u1EBD\u0113\u1E15\u1E17\u0115\u0117\u00EB\u1EBB\u011B\u0205\u0207\u1EB9\u1EC7\u0229\u1E1D\u0119\u1E19\u1E1B\u0247\u025B\u01DD]/g },
57 | { 'base':'f', 'letters':/[\u0066\u24D5\uFF46\u1E1F\u0192\uA77C]/g },
58 | { 'base':'g', 'letters':/[\u0067\u24D6\uFF47\u01F5\u011D\u1E21\u011F\u0121\u01E7\u0123\u01E5\u0260\uA7A1\u1D79\uA77F]/g },
59 | { 'base':'h', 'letters':/[\u0068\u24D7\uFF48\u0125\u1E23\u1E27\u021F\u1E25\u1E29\u1E2B\u1E96\u0127\u2C68\u2C76\u0265]/g },
60 | { 'base':'hv','letters':/[\u0195]/g },
61 | { 'base':'i', 'letters':/[\u0069\u24D8\uFF49\u00EC\u00ED\u00EE\u0129\u012B\u012D\u00EF\u1E2F\u1EC9\u01D0\u0209\u020B\u1ECB\u012F\u1E2D\u0268\u0131]/g },
62 | { 'base':'j', 'letters':/[\u006A\u24D9\uFF4A\u0135\u01F0\u0249]/g },
63 | { 'base':'k', 'letters':/[\u006B\u24DA\uFF4B\u1E31\u01E9\u1E33\u0137\u1E35\u0199\u2C6A\uA741\uA743\uA745\uA7A3]/g },
64 | { 'base':'l', 'letters':/[\u006C\u24DB\uFF4C\u0140\u013A\u013E\u1E37\u1E39\u013C\u1E3D\u1E3B\u017F\u0142\u019A\u026B\u2C61\uA749\uA781\uA747]/g },
65 | { 'base':'lj','letters':/[\u01C9]/g },
66 | { 'base':'m', 'letters':/[\u006D\u24DC\uFF4D\u1E3F\u1E41\u1E43\u0271\u026F]/g },
67 | { 'base':'n', 'letters':/[\u006E\u24DD\uFF4E\u01F9\u0144\u00F1\u1E45\u0148\u1E47\u0146\u1E4B\u1E49\u019E\u0272\u0149\uA791\uA7A5]/g },
68 | { 'base':'nj','letters':/[\u01CC]/g },
69 | { 'base':'o', 'letters':/[\u006F\u24DE\uFF4F\u00F2\u00F3\u00F4\u1ED3\u1ED1\u1ED7\u1ED5\u00F5\u1E4D\u022D\u1E4F\u014D\u1E51\u1E53\u014F\u022F\u0231\u00F6\u022B\u1ECF\u0151\u01D2\u020D\u020F\u01A1\u1EDD\u1EDB\u1EE1\u1EDF\u1EE3\u1ECD\u1ED9\u01EB\u01ED\u00F8\u01FF\u0254\uA74B\uA74D\u0275]/g },
70 | { 'base':'oi','letters':/[\u01A3]/g },
71 | { 'base':'ou','letters':/[\u0223]/g },
72 | { 'base':'oo','letters':/[\uA74F]/g },
73 | { 'base':'p', 'letters':/[\u0070\u24DF\uFF50\u1E55\u1E57\u01A5\u1D7D\uA751\uA753\uA755]/g },
74 | { 'base':'q', 'letters':/[\u0071\u24E0\uFF51\u024B\uA757\uA759]/g },
75 | { 'base':'r', 'letters':/[\u0072\u24E1\uFF52\u0155\u1E59\u0159\u0211\u0213\u1E5B\u1E5D\u0157\u1E5F\u024D\u027D\uA75B\uA7A7\uA783]/g },
76 | { 'base':'s', 'letters':/[\u0073\u24E2\uFF53\u00DF\u015B\u1E65\u015D\u1E61\u0161\u1E67\u1E63\u1E69\u0219\u015F\u023F\uA7A9\uA785\u1E9B]/g },
77 | { 'base':'t', 'letters':/[\u0074\u24E3\uFF54\u1E6B\u1E97\u0165\u1E6D\u021B\u0163\u1E71\u1E6F\u0167\u01AD\u0288\u2C66\uA787]/g },
78 | { 'base':'tz','letters':/[\uA729]/g },
79 | { 'base':'u', 'letters':/[\u0075\u24E4\uFF55\u00F9\u00FA\u00FB\u0169\u1E79\u016B\u1E7B\u016D\u00FC\u01DC\u01D8\u01D6\u01DA\u1EE7\u016F\u0171\u01D4\u0215\u0217\u01B0\u1EEB\u1EE9\u1EEF\u1EED\u1EF1\u1EE5\u1E73\u0173\u1E77\u1E75\u0289]/g },
80 | { 'base':'v', 'letters':/[\u0076\u24E5\uFF56\u1E7D\u1E7F\u028B\uA75F\u028C]/g },
81 | { 'base':'vy','letters':/[\uA761]/g },
82 | { 'base':'w', 'letters':/[\u0077\u24E6\uFF57\u1E81\u1E83\u0175\u1E87\u1E85\u1E98\u1E89\u2C73]/g },
83 | { 'base':'x', 'letters':/[\u0078\u24E7\uFF58\u1E8B\u1E8D]/g },
84 | { 'base':'y', 'letters':/[\u0079\u24E8\uFF59\u1EF3\u00FD\u0177\u1EF9\u0233\u1E8F\u00FF\u1EF7\u1E99\u1EF5\u01B4\u024F\u1EFF]/g },
85 | { 'base':'z', 'letters':/[\u007A\u24E9\uFF5A\u017A\u1E91\u017C\u017E\u1E93\u1E95\u01B6\u0225\u0240\u2C6C\uA763]/g },
86 | ];
87 |
88 | const stripDiacritics = str => {
89 | for (let i = 0; i < map.length; i++) {
90 | str = str.replace(map[i].letters, map[i].base);
91 | }
92 | return str;
93 | };
94 |
95 | export default stripDiacritics;
96 |
--------------------------------------------------------------------------------
/src/utils/trim.js:
--------------------------------------------------------------------------------
1 | const trim = str => str.replace(/^\s+|\s+$/g, '');
2 |
3 | export default trim;
4 |
--------------------------------------------------------------------------------
/test/AsyncCreatable-test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | /* global describe, it, beforeEach */
3 | /* eslint react/jsx-boolean-value: 0 */
4 |
5 | var jsdomHelper = require('../testHelpers/jsdomHelper');
6 | jsdomHelper();
7 | var unexpected = require('unexpected');
8 | var unexpectedDom = require('unexpected-dom');
9 | var unexpectedReact = require('unexpected-react');
10 | var expect = unexpected
11 | .clone()
12 | .installPlugin(unexpectedDom)
13 | .installPlugin(unexpectedReact);
14 |
15 | var React = require('react');
16 | var ReactDOM = require('react-dom');
17 | var TestUtils = require('react-dom/test-utils');
18 | var sinon = require('sinon');
19 | var Select = require('../src');
20 |
21 | describe('AsyncCreatable', () => {
22 | let creatableInstance, creatableNode, filterInputNode, loadOptions;
23 |
24 | beforeEach(() => {
25 | loadOptions = sinon.stub();
26 | });
27 |
28 | function createControl (props = {}) {
29 | props.loadOptions = props.loadOptions || loadOptions;
30 | creatableInstance = TestUtils.renderIntoDocument(
31 |
32 | );
33 | creatableNode = ReactDOM.findDOMNode(creatableInstance);
34 | findAndFocusInputControl();
35 | };
36 |
37 | function findAndFocusInputControl () {
38 | filterInputNode = creatableNode.querySelector('input');
39 | if (filterInputNode) {
40 | TestUtils.Simulate.focus(filterInputNode);
41 | }
42 | };
43 |
44 | it('should create an inner Select', () => {
45 | createControl();
46 | expect(creatableNode, 'to have attributes', {
47 | class: ['Select']
48 | });
49 | });
50 |
51 | it('should render a decorated Select (with passed through properties)', () => {
52 | createControl({
53 | inputProps: {
54 | className: 'foo'
55 | }
56 | });
57 | expect(creatableNode.querySelector('.Select-input'), 'to have attributes', {
58 | class: ['foo']
59 | });
60 | });
61 |
62 | describe('.focus()', () => {
63 | beforeEach(() => {
64 | createControl({});
65 | TestUtils.Simulate.blur(filterInputNode);
66 | });
67 |
68 | it('focuses the search input', () => {
69 | expect(filterInputNode, 'not to equal', document.activeElement);
70 | creatableInstance.focus();
71 | expect(filterInputNode, 'to equal', document.activeElement);
72 | });
73 | });
74 | });
75 |
--------------------------------------------------------------------------------
/test/Option-test.js:
--------------------------------------------------------------------------------
1 | /* global describe, it, beforeEach */
2 |
3 | import helper from '../testHelpers/jsdomHelper';
4 | import React from 'react';
5 | import ReactDOM from 'react-dom';
6 | import sinon from 'sinon';
7 | import TestUtils from 'react-dom/test-utils';
8 | import unexpected from 'unexpected';
9 | import unexpectedDom from 'unexpected-dom';
10 | import unexpectedSinon from 'unexpected-sinon';
11 |
12 | import blockEvent from '../src/utils/blockEvent';
13 |
14 | import Option from '../src/Option';
15 |
16 | helper();
17 | const expect = unexpected
18 | .clone()
19 | .installPlugin(unexpectedSinon)
20 | .installPlugin(unexpectedDom);
21 |
22 | describe('Option component', () => {
23 | let onFocus, onSelect, onUnfocus, instance, node;
24 | const createOption = props => {
25 | onFocus = sinon.spy();
26 | onSelect = sinon.spy();
27 | onUnfocus = sinon.spy();
28 | // Render an instance of the component
29 | instance = TestUtils.renderIntoDocument(
30 |
36 | );
37 |
38 | return instance;
39 | };
40 |
41 | beforeEach(() => {
42 | const props = {
43 | instancePrefix: 'test',
44 | className: 'Wrapper-Class',
45 | children: 'Test Label',
46 | option: {
47 | title: 'testitem',
48 | label: 'testitem',
49 | className: 'Option-Class'
50 | }
51 | };
52 | instance = createOption(props);
53 | node = ReactDOM.findDOMNode(instance);
54 | });
55 |
56 | it('renders the given option', () => {
57 | expect(node.textContent, 'to equal', 'Test Label');
58 | expect(onSelect, 'was not called');
59 | TestUtils.Simulate.mouseDown(node);
60 | expect(onSelect, 'was called');
61 | expect(onFocus, 'was not called');
62 | TestUtils.Simulate.mouseEnter(node);
63 | expect(onFocus, 'was called');
64 | TestUtils.Simulate.mouseMove(node);
65 | expect(onFocus, 'was called');
66 | });
67 |
68 | it('does not focus if Option isFocused already', () => {
69 | const props = {
70 | isFocused: true,
71 | instancePrefix: 'test',
72 | className: 'Wrapper-Class',
73 | children: 'Test Label',
74 | option: {
75 | title: 'testitem',
76 | label: 'testitem',
77 | className: 'Option-Class'
78 | }
79 | };
80 | instance = createOption(props);
81 | node = ReactDOM.findDOMNode(instance);
82 | expect(onFocus, 'was not called');
83 | TestUtils.Simulate.mouseEnter(node);
84 | expect(onFocus, 'was not called');
85 | });
86 |
87 | it('simulates touch events', () => {
88 | expect(instance.dragging, 'to equal', undefined);
89 | // simulate scrolling event
90 | TestUtils.Simulate.touchStart(node);
91 | expect(instance.dragging, 'to equal', false);
92 | TestUtils.Simulate.touchMove(node);
93 | expect(instance.dragging, 'to equal', true);
94 | TestUtils.Simulate.touchEnd(node);
95 | expect(instance.dragging, 'to equal', true);
96 | // simulate touch to select option.
97 | TestUtils.Simulate.touchStart(node);
98 | expect(instance.dragging, 'to equal', false);
99 | expect(onSelect, 'was not called');
100 | TestUtils.Simulate.touchEnd(node);
101 | expect(onSelect, 'was called');
102 | expect(instance.dragging, 'to equal', false);
103 | });
104 |
105 | describe('blockEvent', () => {
106 | let preventDefault, stopPropagation, openStub;
107 | beforeEach(() =>{
108 | preventDefault = sinon.spy();
109 | stopPropagation = sinon.spy();
110 | openStub = sinon.stub(window, 'open');
111 | });
112 |
113 | afterEach(() => {
114 | openStub.restore();
115 | });
116 |
117 | it('should call window.open', () => {
118 | const event = {
119 | target: {
120 | href: 'http://go.com',
121 | tagName: 'A',
122 | target: 'yes',
123 | },
124 | preventDefault,
125 | stopPropagation,
126 | };
127 |
128 | blockEvent(event);
129 |
130 | expect(openStub, 'was called once');
131 | expect(openStub, 'was called with', event.target.href, event.target.target);
132 | });
133 |
134 | it('should set window.location.href and not call window.open', () => {
135 | const event = {
136 | target: {
137 | href: 'http://go.com',
138 | tagName: 'A',
139 | },
140 | preventDefault,
141 | stopPropagation,
142 | };
143 |
144 | Object.defineProperty(window.location, 'href', {
145 | writable: true,
146 | value: 'url'
147 | });
148 |
149 | expect(window.location.href, 'not to equal', event.target.href);
150 |
151 | blockEvent(event);
152 |
153 | expect(window.location.href, 'to equal', event.target.href);
154 | expect(openStub, 'was not called');
155 | });
156 |
157 | it('should return and not call window.open when tagName !=A', () => {
158 | const event = {
159 | target: {
160 | href: 'http://go.com',
161 | tagName: '',
162 | },
163 | preventDefault,
164 | stopPropagation,
165 | };
166 |
167 | Object.defineProperty(window.location, 'href', {
168 | writable: true,
169 | value: 'url'
170 | });
171 |
172 | expect(window.location.href, 'to equal', 'url');
173 |
174 | blockEvent(event);
175 |
176 | expect(window.location.href, 'to equal', 'url');
177 | expect(openStub, 'was not called');
178 | });
179 |
180 | it('should return and not call window.open when no href', () => {
181 | const event = {
182 | target: {
183 | tagName: 'A',
184 | },
185 | preventDefault,
186 | stopPropagation,
187 | };
188 |
189 | Object.defineProperty(window.location, 'href', {
190 | writable: true,
191 | value: 'url'
192 | });
193 |
194 | expect(window.location.href, 'to equal', 'url');
195 |
196 | blockEvent(event);
197 |
198 | expect(window.location.href, 'to equal', 'url');
199 | expect(openStub, 'was not called');
200 | });
201 | });
202 | });
203 |
--------------------------------------------------------------------------------
/test/Value-test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | /* global describe, it, beforeEach */
3 |
4 | var helper = require('../testHelpers/jsdomHelper');
5 | helper();
6 |
7 | var unexpected = require('unexpected');
8 | var unexpectedDom = require('unexpected-dom');
9 | var unexpectedSinon = require('unexpected-sinon');
10 | var sinon = require('sinon');
11 |
12 | var expect = unexpected
13 | .clone()
14 | .installPlugin(unexpectedSinon)
15 | .installPlugin(unexpectedDom);
16 |
17 | var React = require('react');
18 | var ReactDOM = require('react-dom');
19 | var TestUtils = require('react-dom/test-utils');
20 |
21 | var OPTION = { label: 'TEST-LABEL', value: 'TEST-VALUE' };
22 |
23 | var Value = require('../src/Value').default;
24 |
25 | describe('Value component', function() {
26 |
27 | var props;
28 | var value;
29 |
30 | beforeEach(function() {
31 | props = {
32 | value: OPTION,
33 | onRemove: sinon.spy()
34 | };
35 | value = TestUtils.renderIntoDocument({OPTION.label} );
36 | });
37 |
38 | it('requests its own removal when the remove icon is clicked', function() {
39 | var selectItemIcon = TestUtils.findRenderedDOMComponentWithClass(value, 'Select-value-icon');
40 | TestUtils.Simulate.mouseDown(selectItemIcon);
41 | expect(props.onRemove, 'was called');
42 | });
43 |
44 | it('requests its own removal when the remove icon is touched', function() {
45 | var selectItemIcon = TestUtils.findRenderedDOMComponentWithClass(value, 'Select-value-icon');
46 | TestUtils.Simulate.touchStart(selectItemIcon);
47 | TestUtils.Simulate.touchEnd(selectItemIcon);
48 | expect(props.onRemove, 'was called');
49 | });
50 |
51 | it('ignores its own removal when the remove icon is touched and dragged', function() {
52 | var selectItemIcon = TestUtils.findRenderedDOMComponentWithClass(value, 'Select-value-icon');
53 | TestUtils.Simulate.touchStart(selectItemIcon);
54 | TestUtils.Simulate.touchMove(selectItemIcon);
55 | TestUtils.Simulate.touchEnd(selectItemIcon);
56 | expect(props.onRemove, 'was not called');
57 | });
58 |
59 | describe('without a custom click handler', function() {
60 |
61 | it('presents the given label', function() {
62 | var selectItemLabel = TestUtils.findRenderedDOMComponentWithClass(value, 'Select-value-label');
63 | expect(ReactDOM.findDOMNode(selectItemLabel), 'to have text', OPTION.label);
64 | });
65 |
66 | });
67 |
68 | describe('with a custom click handler', function() {
69 | var valueLabel;
70 |
71 | beforeEach(function() {
72 | props = {
73 | value: OPTION,
74 | onRemove: sinon.spy(),
75 | onClick: sinon.spy(),
76 | };
77 | value = TestUtils.renderIntoDocument({OPTION.label} );
78 | valueLabel = TestUtils.findRenderedDOMComponentWithClass(value, 'Select-value-label');
79 | });
80 |
81 | it('presents the given label', function() {
82 | expect(ReactDOM.findDOMNode(valueLabel), 'to have text', OPTION.label);
83 | });
84 |
85 | it('calls a custom callback when the anchor is clicked', function() {
86 | TestUtils.Simulate.mouseDown(valueLabel, { button: 0 });
87 | expect(props.onClick, 'was called');
88 | });
89 |
90 | it('calls a custom callback when the anchor is touched', function() {
91 | TestUtils.Simulate.touchEnd(valueLabel);
92 | expect(props.onClick, 'was called');
93 | });
94 |
95 | it('does not call a custom callback when the anchor is clicked and button !== 0', function() {
96 | TestUtils.Simulate.mouseDown(valueLabel, { button: 2 });
97 | expect(props.onClick, 'was not called');
98 | });
99 |
100 | });
101 |
102 | describe('handleMouseDown', function() {
103 | var event;
104 |
105 | it('should stop propagation when value has href and no onClick in props', function () {
106 | props = {
107 | value: { href: '1' },
108 | };
109 | event = { stopPropagation: sinon.spy() };
110 | value = TestUtils.renderIntoDocument({OPTION.label} );
111 | value.handleMouseDown(event);
112 | expect(event.stopPropagation, 'was called once');
113 | });
114 |
115 | it('should return when value does not have href and no onClick in props', function () {
116 | props = {
117 | value: OPTION,
118 | };
119 | event = { stopPropagation: sinon.spy() };
120 | value = TestUtils.renderIntoDocument({OPTION.label} );
121 | value.handleMouseDown(event);
122 | expect(event.stopPropagation, 'was not called');
123 | });
124 |
125 | });
126 |
127 | });
128 |
--------------------------------------------------------------------------------
/testHelpers/jsdomHelper.js:
--------------------------------------------------------------------------------
1 |
2 | module.exports = function (html) {
3 | if (typeof document !== 'undefined') {
4 | return;
5 | }
6 |
7 | var jsdom = require('jsdom').jsdom;
8 | global.document = jsdom(html || '');
9 | global.window = global.document.defaultView;
10 | global.navigator = {
11 | userAgent: 'JSDOM'
12 | };
13 | };
14 |
--------------------------------------------------------------------------------
/testHelpers/nodeListType.js:
--------------------------------------------------------------------------------
1 |
2 | module.exports = {
3 |
4 | name: 'fix-nodelist-for-jsdom-6',
5 |
6 | installInto: function (expect) {
7 |
8 | expect.addType({
9 | name: 'NodeList',
10 | base: 'array-like',
11 | identify: value => {
12 | return typeof window !== 'undefined' &&
13 | typeof window.NodeList === 'function' &&
14 | value instanceof window.NodeList;
15 | }
16 | });
17 |
18 | }
19 | };
20 |
--------------------------------------------------------------------------------
/wallaby.js:
--------------------------------------------------------------------------------
1 | /*
2 | This is the config file for the [Wallabyjs](http://wallabyjs.com) test runner
3 | */
4 |
5 | var babel = require('babel');
6 |
7 | module.exports = function (wallaby) { // eslint-disable-line no-unused-vars
8 | return {
9 | files: ['src/**/*.js', {
10 | pattern: 'testHelpers/*.js',
11 | instrument: false
12 | }],
13 | tests: ['test/*-test.js' ],
14 | env: {
15 | type: 'node',
16 | runner: 'node'
17 | },
18 | compilers: {
19 | '**/*.js': wallaby.compilers.babel({
20 | babel: babel
21 | })
22 | }
23 | };
24 | };
25 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const webpack = require('webpack');
2 | const path = require('path');
3 | const HtmlWebpackPlugin = require('html-webpack-plugin');
4 | const ExtractTextPlugin = require('extract-text-webpack-plugin');
5 |
6 | module.exports = {
7 | context: path.resolve(__dirname, 'examples/src'),
8 | entry: {
9 | app: './app.js',
10 | },
11 | output: {
12 | path: path.resolve(__dirname, 'examples/dist'),
13 | filename: '[name].js',
14 | publicPath: '/',
15 | },
16 | devServer: {
17 | contentBase: path.resolve(__dirname, 'examples/src'),
18 | port: 8000,
19 | },
20 | module: {
21 | rules: [
22 | {
23 | test: /\.js$/,
24 | exclude: [/node_modules/],
25 | use: [{
26 | loader: 'babel-loader',
27 | options: { presets: ['env'] },
28 | }],
29 | },
30 | {
31 | test: /\.less$/,
32 | use: ExtractTextPlugin.extract({
33 | fallback: 'style-loader',
34 | use: ['css-loader', 'less-loader'],
35 | })
36 | },
37 | {
38 | test: /\.html$/,
39 | use: [
40 | {
41 | loader: 'html-loader',
42 | }
43 | ]
44 | },
45 | ],
46 | },
47 | resolve: {
48 | alias: {
49 | 'react-select-plus': path.resolve(__dirname, 'src/index'),
50 | }
51 | },
52 | plugins: [
53 | new webpack.optimize.CommonsChunkPlugin({
54 | name: 'common',
55 | filename: 'common.js',
56 | minChunk: 2,
57 | }),
58 | new HtmlWebpackPlugin({
59 | filename: 'index.html',
60 | inject: false,
61 | template: path.resolve(__dirname, 'examples/src/index.html')
62 | }),
63 | new ExtractTextPlugin('example.css'),
64 | ]
65 | };
66 |
--------------------------------------------------------------------------------