react-select-input-v2 1.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2018 Samrith Shankar
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.
package/README.md ADDED
@@ -0,0 +1,82 @@
1
+ # react-select-input
2
+
3
+ [![Travis](https://img.shields.io/travis/samrith-s/react-select-input/master.svg)][build]
4
+ [![npm package](https://img.shields.io/npm/v/react-select-input.svg)][npm]
5
+ [![GitHub license](https://img.shields.io/github/license/samrith-s/react-select-input.svg)][license]
6
+
7
+ A simple combination of traditional HTML5 input and select.
8
+
9
+ ## v2 underway!
10
+
11
+ I've left this project inactive for way too long (`v1.0.0` was launched a year after initial release!). Going through the code, there are a lot of stuff that could be improved. For all the users, the entire project will undergo a complete rewrite. Many of these changes will be breaking, leading up to the release of v2.
12
+
13
+ **Head over to the [Version 2 thread](https://github.com/samrith-s/react-select-input/issues/2) to know more and discuss!**
14
+
15
+ ### Props Overview
16
+
17
+ ```javascript
18
+ InputSelect.defaultProps = {
19
+ uniqueKey: 'react-select-input', //String
20
+ style: null, //Object
21
+ value: '', //String
22
+ valueKey: 'value', //String
23
+ labelKey: 'label', //String
24
+ placeholder: 'Enter text', //String
25
+ className: '', //String
26
+ openUp: false, //Boolean
27
+ disableEnter: true, //Boolean
28
+ collapseOnBlur: true, //Boolean
29
+ collapseOnEscape: true, //Boolean
30
+ collapseOnSelect: true, //Boolean
31
+ autoFocus: true, //Boolean
32
+ clearable: true, //Boolean - NO EFFECT
33
+ options: [], //Array
34
+ onSelect: undefined, //Function (option)
35
+ onClear: undefined, // Function
36
+ onChange: undefined, //Function (event)
37
+ onFocus: undefined, //Function (event)
38
+ onBlur: undefined, //Function (event)
39
+ onKeyUp: undefined, //Function (event)
40
+ onKeyDown: undefined, //Function (event)
41
+ noOptions: undefined //JSX
42
+ };
43
+ ```
44
+
45
+ ### Options
46
+
47
+ | Prop | Type | Default |
48
+ | ---------------- | :-----: | --------------------------------------------------------------------------------------------- |
49
+ | uniqueKey | String | `"react-select-input"` Unique key for the component. |
50
+ | style | Object | `null` Any custom inline styles that need to be passed. |
51
+ | value | String | `""` The value to either populate or control the component. |
52
+ | valueKey | String | `"value"` The key from your options which you want to use as the value key. Should be unique. |
53
+ | labelKey | String | `"label"` The key from your options for pretty display of options. |
54
+ | placeholder | String | `"Enter text"` Placeholder for input. |
55
+ | className | String | `""` Custom classes apart from the default classes to the wrapper. |
56
+ | openUp | Boolean | `false` Whether the select should work as a dropup or dropdown. |
57
+ | disableEnter | Boolean | `true` If the default return key behaviour should be preserved. |
58
+ | collapseOnBlur | Boolean | `true` Clicking outside the ref should collapse the select. |
59
+ | collapseOnEscape | Boolean | `true` While focused, hitting Escape collapse the select. |
60
+ | collapseOnSelect | Boolean | `true` Upon selection collapses the select. |
61
+ | autoFocus | Boolean | `true` On mount, focus the input field. |
62
+ | clearable | Boolean | `true` Show an icon to clear the entire select. |
63
+ | options | Array | `[ ]` Array of options to use while rendering the list. |
64
+ | noOptions | JSX | `undefined` JSX to render when no option matches search. |
65
+
66
+ ### Methods
67
+
68
+ | Prop | Parameters | Description |
69
+ | --------- | :--------: | --------------------------------------------------------------------------------------------- |
70
+ | onChange | event | Synthetic event of the input upon change. |
71
+ | onSelect | option | The selected option.<br />Gets triggered during both clicking and arrow navigation selection. |
72
+ | onFocus | event | Synthetic event of the input upon focus. |
73
+ | onBlur | event | Synthetic event of the input upon blur. Native function passed. |
74
+ | onKeyUp | event | Synthetic event of the input upon keyUp. |
75
+ | onKeyDown | event | Synthetic event of the input upon keyDown. |
76
+ | onClear | none | A callback after clear if `clearable`. |
77
+
78
+ Copyright &copy; 2018.
79
+
80
+ [build]: https://travis-ci.org/samrith-s/react-select-input
81
+ [npm]: https://www.npmjs.org/package/react-select-input
82
+ [license]: https://github.com/samrith-s/react-select-input/blob/master/LICENSE
package/es/index.js ADDED
@@ -0,0 +1,316 @@
1
+ function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
2
+
3
+ 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; }
4
+
5
+ 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; }
6
+
7
+ import React, { Component } from 'react';
8
+
9
+ import Textarea from 'react-autosize-textarea';
10
+
11
+ function escapeRegExp(string) {
12
+ var reRegExpChar = /[\\^$.*+?()[\]{}|]/g;
13
+ var reHasRegExpChar = RegExp(reRegExpChar.source);
14
+ return string && reHasRegExpChar.test(string) ? string.replace(reRegExpChar, '\\$&') : string || '';
15
+ }
16
+
17
+ var SelectInput = function (_Component) {
18
+ _inherits(SelectInput, _Component);
19
+
20
+ function SelectInput() {
21
+ _classCallCheck(this, SelectInput);
22
+
23
+ var _this = _possibleConstructorReturn(this, _Component.call(this));
24
+
25
+ _this.renderOptions = function (options) {
26
+ return options && options.length > 0 ? options.map(function (option, index) {
27
+ return React.createElement(
28
+ 'div',
29
+ {
30
+ className: 'ris-option' + (_this.state.selectedOption === option[_this.props.valueKey] ? ' selected' : '') + (_this.state.currentOption === index ? ' current' : ''),
31
+ key: _this.props.uniqueKey + '-option-' + index,
32
+ onClick: _this.handleClick.bind(_this, index),
33
+ ref: function ref(_ref) {
34
+ return _this['option-' + option[_this.props.valueKey]] = _ref;
35
+ }
36
+ },
37
+ option[_this.props.labelKey]
38
+ );
39
+ }) : null;
40
+ };
41
+
42
+ _this.handleSelect = function (option, index) {
43
+ var state = _this.manipState(_this.state, 'value', option[_this.props.labelKey]);
44
+ state = _this.manipState(state, 'selectedOption', option[_this.props.valueKey]);
45
+ state = _this.manipState(state, 'currentOption', -1);
46
+ if (_this.props.collapseOnSelect) state = _this.setIsOpen(state, false);
47
+ state = _this.manipState(state, 'searchMatchOptions', _this.matchingOptions(_this.props.options, state.value));
48
+ _this.setState(state);
49
+
50
+ if (_this.isFunction(_this.props.onSelect)) _this.props.onSelect(option);
51
+ };
52
+
53
+ _this.handleClick = function (index, event) {
54
+ var option = _this.pickOption(index);
55
+ _this.handleSelect(option, index);
56
+ };
57
+
58
+ _this.handleChange = function (event) {
59
+ var state = _this.manipState(_this.state, 'value', event.target.value);
60
+ state = _this.manipState(state, 'searchMatchOptions', _this.matchingOptions(_this.props.options, event.target.value));
61
+ state = _this.setIsOpen(state, true);
62
+
63
+ if (!state.value) {
64
+ state = _this.manipState(state, 'currentOption', -1);
65
+ state = _this.manipState(state, 'selectedOption', null);
66
+ }
67
+
68
+ if (_this.isFunction(_this.props.onChange)) _this.props.onChange(event);
69
+
70
+ _this.setState(state);
71
+ };
72
+
73
+ _this.handleFocus = function (event) {
74
+ var state = _this.setIsOpen(_this.state, true);
75
+ state = _this.manipState(state, 'searchMatchOptions', _this.matchingOptions(_this.props.options, event.target.value));
76
+ _this.setState(state);
77
+
78
+ if (_this.isFunction(_this.props.onFocus)) _this.props.onFocus(event);
79
+ };
80
+
81
+ _this.handleClear = function () {
82
+ var value = '';
83
+ var state = _this.manipState(_this.state, 'value', value);
84
+ state = _this.manipState(state, 'searchMatchOptions', _this.matchingOptions(_this.props.options, state.value));
85
+ state = _this.manipState(state, 'currentOption', -1);
86
+ state = _this.manipState(state, 'selectedOption', null);
87
+ _this.setState(state);
88
+
89
+ if (_this.isFunction(_this.props.onClear)) _this.props.onClear();
90
+ };
91
+
92
+ _this.handleBlur = function (event) {
93
+ if (_this.props.collapseOnBlur) {
94
+ var state = _this.setIsOpen(_this.state, false);
95
+ _this.setState(state);
96
+ }
97
+
98
+ if (_this.isFunction(_this.props.onBlur)) _this.props.onBlur(event);
99
+ };
100
+
101
+ _this.handleKeyUp = function (event) {
102
+ if (event.key === 'Enter' && _this.state.currentOption > -1) {
103
+ _this.handleSelect(_this.pickOption());
104
+ } else if (event.key === 'Enter' && _this.state.currentOption === -1) {
105
+ _this.handleSelect(_this.pickOption());
106
+ }
107
+
108
+ if (event.key === 'Escape' && _this.props.collapseOnEscape) {
109
+ _this.setState(_this.setIsOpen(_this.state, false));
110
+ }
111
+
112
+ if (_this.isFunction(_this.props.onKeyUp)) _this.props.onKeyUp(event);
113
+ };
114
+
115
+ _this.handleKeyDown = function (event) {
116
+ if (event.key === 'Enter' && _this.props.disableEnter) event.preventDefault();
117
+
118
+ if (event.key === 'Escape' && _this.props.collapseOnEscape) {
119
+ var state = _this.setIsOpen(_this.state, false);
120
+ _this.setState(state);
121
+ }
122
+
123
+ _this.handleOptionNavigation(event);
124
+
125
+ if (_this.isFunction(_this.props.onKeyDown)) _this.props.onKeyDown(event);
126
+ };
127
+
128
+ _this.handleOptionNavigation = function (event) {
129
+ var state = {};
130
+ var currentOption = _this.state.currentOption;
131
+
132
+ if (event.key === 'ArrowUp' || event.key === 'ArrowDown' && _this.disableEnter) event.preventDefault();
133
+
134
+ if (event.key === 'ArrowUp' && currentOption === -1 && _this.props.openUp) currentOption = _this.state.searchMatchOptions.length;
135
+
136
+ if (_this.props.openUp) {
137
+ if (event.key === 'ArrowDown') {
138
+ if (currentOption > -1) currentOption++;else currentOption = -1;
139
+ } else if (event.key === 'ArrowUp') currentOption--;
140
+ } else {
141
+ if (event.key === 'ArrowDown') currentOption++;else if (event.key === 'ArrowUp') currentOption--;
142
+ }
143
+
144
+ if (currentOption < -1) currentOption = -1;else if (currentOption > _this.state.searchMatchOptions.length - 1 && !_this.props.openUp) currentOption = _this.state.searchMatchOptions.length - 1;else if (currentOption > _this.state.searchMatchOptions.length - 1 && _this.props.openUp) currentOption = -1;
145
+
146
+ state = _this.manipState(_this.state, 'currentOption', currentOption);
147
+ var optionRef = currentOption > -1 ? _this['option-' + _this.state.searchMatchOptions[currentOption][_this.props.valueKey]] : null;
148
+ if (optionRef) optionRef.scrollIntoViewIfNeeded(false);
149
+
150
+ _this.setState(state);
151
+ };
152
+
153
+ _this.matchingOptions = function (options, value) {
154
+ value = escapeRegExp(value);
155
+ var searchValue = value.replace(/\s/g, '');
156
+ var searchOptions = [];
157
+
158
+ if (options && options.length > 0 && value) searchOptions = options.filter(function (option, index) {
159
+ var regexp = new RegExp(searchValue, 'gi');
160
+ var value = option[_this.props.valueKey] || '';
161
+ var label = option[_this.props.labelKey] || '';
162
+ return regexp.test(label.replace(/\s/g, '')) || regexp.test(value.replace(/\s/g, ''));
163
+ });else searchOptions = options.slice(0, options.length);
164
+
165
+ if (_this.props.openUp) searchOptions.reverse();
166
+
167
+ return searchOptions;
168
+ };
169
+
170
+ _this.pickOption = function (index) {
171
+ var _ref2;
172
+
173
+ index = index !== null && index !== undefined ? index : _this.state.currentOption;
174
+ if (index > -1) return _this.state.searchMatchOptions[index];else return _ref2 = {}, _ref2[_this.props.labelKey] = _this.input.value.trim(), _ref2[_this.props.valueKey] = _this.input.value.trim(), _ref2;
175
+ };
176
+
177
+ _this.setIsOpen = function (state, show) {
178
+ return Object.assign({}, state, {
179
+ isOpen: show || false
180
+ });
181
+ };
182
+
183
+ _this.manipState = function (state, key, value) {
184
+ var _Object$assign;
185
+
186
+ return Object.assign({}, state, (_Object$assign = {}, _Object$assign[key] = value, _Object$assign));
187
+ };
188
+
189
+ _this.isFunction = function (obj) {
190
+ return typeof obj === 'function';
191
+ };
192
+
193
+ _this.handleOutsideClick = function (event) {
194
+ if (!_this.ris.contains(event.target) && _this.props.collapseOnBlur) _this.setState(_this.setIsOpen(_this.state, false));
195
+ };
196
+
197
+ _this.state = {
198
+ isOpen: false,
199
+ value: null,
200
+ currentOption: -1,
201
+ selectedOption: null,
202
+ searchMatchOptions: []
203
+ };
204
+ return _this;
205
+ }
206
+
207
+ SelectInput.prototype.componentDidMount = function componentDidMount() {
208
+ document.addEventListener('click', this.handleOutsideClick);
209
+ };
210
+
211
+ SelectInput.prototype.componentDidUpdate = function componentDidUpdate() {
212
+ if (this.props.openUp && this.state.currentOption === -1 && this.state.isOpen && !this.state.selectedOption) {
213
+ this.options.scrollTop = this.options.scrollHeight;
214
+ } else if (this.props.openUp && this.state.selectedOption && this.state.isOpen) {
215
+ this['option-' + this.state.selectedOption].scrollIntoViewIfNeeded(true);
216
+ }
217
+ };
218
+
219
+ SelectInput.prototype.componentWillUnmount = function componentWillUnmount() {
220
+ document.removeEventListener('click', this.handleOutsideClick);
221
+ };
222
+
223
+ /*
224
+ Various renderers
225
+ */
226
+
227
+ /*
228
+ Handle different events
229
+ */
230
+
231
+ /*
232
+ Searching function
233
+ */
234
+
235
+ /*
236
+ Various helpers
237
+ */
238
+
239
+ SelectInput.prototype.render = function render() {
240
+ var _this2 = this;
241
+
242
+ return React.createElement(
243
+ 'div',
244
+ {
245
+ className: 'ris' + (this.props.openUp ? ' ris-open-up' : '') + (this.props.clearable ? ' ris-is-clearable' : '') + (this.props.className ? ' ' + this.props.className : ''),
246
+ key: 'ris-' + this.props.uniqueKey,
247
+ ref: function ref(_ref4) {
248
+ return _this2.ris = _ref4;
249
+ },
250
+ style: this.props.style
251
+ },
252
+ React.createElement(Textarea, {
253
+ className: 'ris-input',
254
+ value: this.state.value !== null ? this.state.value : this.props.value,
255
+ placeholder: this.props.placeholder,
256
+ onChange: this.handleChange,
257
+ onFocus: this.handleFocus,
258
+ onBlur: this.props.onBlur,
259
+ onKeyUp: this.handleKeyUp,
260
+ onKeyDown: this.handleKeyDown,
261
+ autoFocus: this.props.autoFocus,
262
+ innerRef: function innerRef(ref) {
263
+ return _this2.input = ref;
264
+ }
265
+ }),
266
+ this.props.clearable ? React.createElement(
267
+ 'div',
268
+ { className: 'ris-clearable', onClick: this.handleClear },
269
+ 'x'
270
+ ) : null,
271
+ this.state.isOpen ? this.state.searchMatchOptions.length > 0 ? React.createElement(
272
+ 'div',
273
+ { className: 'ris-options', ref: function ref(_ref3) {
274
+ return _this2.options = _ref3;
275
+ } },
276
+ this.renderOptions(this.state.searchMatchOptions)
277
+ ) : this.props.noOptions ? React.createElement(
278
+ 'div',
279
+ { className: 'ris-options ris-no-options' },
280
+ this.props.noOptions
281
+ ) : null : null
282
+ );
283
+ };
284
+
285
+ return SelectInput;
286
+ }(Component);
287
+
288
+ export { SelectInput as default };
289
+
290
+
291
+ SelectInput.defaultProps = {
292
+ uniqueKey: 'react-select-input', //String
293
+ style: null, //Object
294
+ ref: null, //Function
295
+ value: '', //String
296
+ valueKey: 'value', //String
297
+ labelKey: 'label', //String
298
+ placeholder: 'Enter text', //String
299
+ className: '', //String
300
+ openUp: false, //Boolean
301
+ disableEnter: true, //Boolean
302
+ collapseOnBlur: true, //Boolean
303
+ collapseOnEscape: true, //Boolean
304
+ collapseOnSelect: true, //Boolean
305
+ autoFocus: true, //Boolean
306
+ clearable: true, //Boolean
307
+ options: [], //Array
308
+ onChange: undefined, //Function
309
+ onSelect: undefined, //Function
310
+ onFocus: undefined, //Function
311
+ onBlur: undefined, //Function
312
+ onKeyUp: undefined, //Function
313
+ onKeyDown: undefined, //Function
314
+ onClear: undefined, //Function
315
+ noOptions: undefined //JSX
316
+ };
@@ -0,0 +1,92 @@
1
+
2
+
3
+ .ris {
4
+ display: block;
5
+ position: relative;
6
+ box-sizing: border-box;
7
+ font-size:12px;
8
+ }
9
+
10
+ .ris * {
11
+ box-sizing: border-box;
12
+ }
13
+
14
+ .ris.ris-is-clearable .ris-clearable {
15
+ position: absolute;
16
+ top: 50%;
17
+ right: 5px;
18
+ font-size:1.1em;
19
+ margin-top:-1px;
20
+ transform: translate(0, -50%);
21
+ padding:5px;
22
+ color: #555;
23
+ cursor:pointer;
24
+ }
25
+
26
+ .ris.ris-is-creatable .ris-input {
27
+ padding-right:15px;
28
+ }
29
+
30
+ .ris .ris-input {
31
+ display: block;
32
+ resize: none;
33
+ width: 100%;
34
+ padding: 5px;
35
+ border: 5px solid #eee;
36
+ border-radius: 5px;
37
+ font-size:inherit;
38
+ }
39
+
40
+ .ris .ris-input:active, .ris .ris-input:focus {
41
+ outline: 0;
42
+ border: 5px solid #1e88e5;
43
+ }
44
+
45
+ .ris.ris-open-up .ris-options {
46
+ top:0;
47
+ transform:translate(-50%,-100%);
48
+ }
49
+
50
+ .ris .ris-options {
51
+ position: absolute;
52
+ top: 100%;
53
+ left: 50%;
54
+ margin:0;
55
+ width: calc(100% - 10px);
56
+ background: #fff;
57
+ transform:translate(-50%, 0);
58
+ border: 1px solid #eee;
59
+ overflow: auto;
60
+ box-shadow: 0 2px 3px 1px #eee;
61
+ max-height:245px;
62
+ font-size:inherit;
63
+ }
64
+
65
+ .ris .ris-options .ris-option {
66
+ padding: 5px;
67
+ cursor: pointer;
68
+ border-bottom: 1px solid #efefef;
69
+ }
70
+
71
+ .ris .ris-options .ris-option:last-child {
72
+ border-bottom: 0;
73
+ }
74
+
75
+ .ris .ris-options .ris-option:hover {
76
+ background: #bbdefb;
77
+ }
78
+
79
+ .ris .ris-options .ris-option.current {
80
+ background:#bbdefb;
81
+ }
82
+
83
+ .ris .ris-options .ris-option.selected {
84
+ background:#1e88e5;
85
+ color: #fff;
86
+ }
87
+
88
+ .ris .ris-options.ris-no-options {
89
+ padding:5px;
90
+ text-align:center;
91
+ color:#ccc;
92
+ }