From e0f0c28d188c1cedebdfd7ec5ebe04ce11859914 Mon Sep 17 00:00:00 2001 From: Vagner Santana Date: Wed, 3 Jun 2015 11:00:36 -0300 Subject: [PATCH 1/5] Add icon --- src/Select.js | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/Select.js b/src/Select.js index ca53283af0..745898bc83 100644 --- a/src/Select.js +++ b/src/Select.js @@ -651,9 +651,22 @@ var Select = React.createClass({ var mouseDown = this.selectValue.bind(this, op); if (op.disabled) { - return
{op.label}
; + return ( +
+ {op.label} +
+ ); } else { - return
{ op.create ? 'Add ' + op.label + ' ?' : op.label}
; + return ( +
+ { op.create ? 'Add ' + op.label + ' ?' : op.label} +
+ ); } }, this); From f96d52bc5773198362088f02677b3c2469278094 Mon Sep 17 00:00:00 2001 From: Vagner Santana Date: Wed, 3 Jun 2015 11:07:57 -0300 Subject: [PATCH 2/5] build:lib - add icon --- gulpfile.js | 30 ++++++++++++++++++++ lib/Select.js | 78 ++++++++++++++++++++++++++++++++++++++++++--------- package.json | 4 +++ 3 files changed, 98 insertions(+), 14 deletions(-) diff --git a/gulpfile.js b/gulpfile.js index 1d6ad18f9f..d8d6de5b7c 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -31,3 +31,33 @@ var taskConfig = { }; initGulpTasks(gulp, taskConfig); + + +/* ========================================================================== + Lib + ========================================================================== */ + +var babel = require('gulp-babel'); +var del = require('del'); + +gulp.task('clean:lib', function(done) { + del('./lib', done); +}); + +gulp.task('build:lib', function() { + return gulp.src([ + './src/**/*.js', + '!**/__tests__/**/*' + ]) + .pipe(babel({ + plugins: [require('babel-plugin-object-assign')] + })) + .pipe(gulp.dest('./lib')); +}); + +gulp.task('watch:lib', ['build:lib'], function() { + return gulp.watch([ + './src/**/*.js', + '!**/__tests__/**/*' + ], ['build:lib']); +}); diff --git a/lib/Select.js b/lib/Select.js index e34513e48a..80c3e24ade 100644 --- a/lib/Select.js +++ b/lib/Select.js @@ -37,8 +37,9 @@ var Select = React.createClass({ filterOptions: React.PropTypes.func, // method to filter the options array: function([options], filterString, [values]) matchPos: React.PropTypes.string, // (any|start) match the start or entire string when filtering matchProp: React.PropTypes.string, // (any|label|value) which option property to filter on + ignoreCase: React.PropTypes.bool, // whether to perform case-insensitive filtering inputProps: React.PropTypes.object, // custom attributes for the Input (in the Select-control) e.g: {'data-foo': 'bar'} - + allowCreate: React.PropTypes.bool, // wether to allow creation of new entries /* * Allow user to make option label clickable. When this handler is defined we should * wrap label into label tag. @@ -69,7 +70,9 @@ var Select = React.createClass({ className: undefined, matchPos: 'any', matchProp: 'any', + ignoreCase: true, inputProps: {}, + allowCreate: false, onOptionLabelClick: undefined }; @@ -96,11 +99,6 @@ var Select = React.createClass({ componentWillMount: function componentWillMount() { this._optionsCache = {}; this._optionsFilterString = ''; - this.setState(this.getStateFromValue(this.props.value)); - - if (this.props.asyncOptions && this.props.autoload) { - this.autoloadAsyncOptions(); - } var self = this; this._closeMenuIfClickedOutside = function (event) { @@ -122,12 +120,27 @@ var Select = React.createClass({ }; this._bindCloseMenuIfClickedOutside = function () { - document.addEventListener('click', self._closeMenuIfClickedOutside); + if (!document.addEventListener && document.attachEvent) { + document.attachEvent('onclick', this._closeMenuIfClickedOutside); + } else { + document.addEventListener('click', this._closeMenuIfClickedOutside); + } }; this._unbindCloseMenuIfClickedOutside = function () { - document.removeEventListener('click', self._closeMenuIfClickedOutside); + if (!document.removeEventListener && document.detachEvent) { + document.detachEvent('onclick', this._closeMenuIfClickedOutside); + } else { + document.removeEventListener('click', this._closeMenuIfClickedOutside); + } }; + + this.setState(this.getStateFromValue(this.props.value), function () { + //Executes after state change is done. Fixes issue #201 + if (this.props.asyncOptions && this.props.autoload) { + this.autoloadAsyncOptions(); + } + }); }, componentWillUnmount: function componentWillUnmount() { @@ -281,7 +294,7 @@ var Select = React.createClass({ }, resetValue: function resetValue() { - this.setValue(this.state.value); + this.setValue(this.state.value === '' ? null : this.state.value); }, getInputNode: function getInputNode() { @@ -371,6 +384,8 @@ var Select = React.createClass({ case 13: // enter + if (!this.state.isOpen) return; + this.selectFocusedOption(); break; @@ -393,6 +408,15 @@ var Select = React.createClass({ this.focusNextOption(); break; + case 188: + // , + if (this.props.allowCreate) { + event.preventDefault(); + event.stopPropagation(); + this.selectFocusedOption(); + } + break; + default: return; } @@ -464,7 +488,7 @@ var Select = React.createClass({ } } this.setState(newState); - if (callback) callback({}); + if (callback) callback.call(this, {}); return; } } @@ -493,7 +517,7 @@ var Select = React.createClass({ } self.setState(newState); - if (callback) callback({}); + if (callback) callback.call(self, {}); }); }, @@ -514,13 +538,21 @@ var Select = React.createClass({ if (this.props.filterOption) return this.props.filterOption.call(this, op, filterValue); var valueTest = String(op.value), labelTest = String(op.label); - return !filterValue || this.props.matchPos === 'start' ? this.props.matchProp !== 'label' && valueTest.toLowerCase().substr(0, filterValue.length) === filterValue || this.props.matchProp !== 'value' && labelTest.toLowerCase().substr(0, filterValue.length) === filterValue : this.props.matchProp !== 'label' && valueTest.toLowerCase().indexOf(filterValue.toLowerCase()) >= 0 || this.props.matchProp !== 'value' && labelTest.toLowerCase().indexOf(filterValue.toLowerCase()) >= 0; + if (this.props.ignoreCase) { + valueTest = valueTest.toLowerCase(); + labelTest = labelTest.toLowerCase(); + filterValue = filterValue.toLowerCase(); + } + return !filterValue || this.props.matchPos === 'start' ? this.props.matchProp !== 'label' && valueTest.substr(0, filterValue.length) === filterValue || this.props.matchProp !== 'value' && labelTest.substr(0, filterValue.length) === filterValue : this.props.matchProp !== 'label' && valueTest.indexOf(filterValue) >= 0 || this.props.matchProp !== 'value' && labelTest.indexOf(filterValue) >= 0; }; return (options || []).filter(filterOption, this); } }, selectFocusedOption: function selectFocusedOption() { + if (this.props.allowCreate && !this.state.focusedOption) { + return this.selectValue(this.state.inputValue); + } return this.selectValue(this.state.focusedOption); }, @@ -596,6 +628,15 @@ var Select = React.createClass({ if (this.state.filteredOptions.length > 0) { focusedValue = focusedValue == null ? this.state.filteredOptions[0] : focusedValue; } + // Add the current value to the filtered options in last resort + if (this.props.allowCreate && this.state.inputValue.trim()) { + var inputValue = this.state.inputValue; + this.state.filteredOptions.unshift({ + value: inputValue, + label: inputValue, + create: true + }); + } var ops = Object.keys(this.state.filteredOptions).map(function (key) { var op = this.state.filteredOptions[key]; @@ -617,13 +658,22 @@ var Select = React.createClass({ return React.createElement( 'div', { ref: ref, key: 'option-' + op.value, className: optionClass }, + React.createElement('i', { className: op.iconClass }), + ' ', op.label ); } else { return React.createElement( 'div', - { ref: ref, key: 'option-' + op.value, className: optionClass, onMouseEnter: mouseEnter, onMouseLeave: mouseLeave, onMouseDown: mouseDown, onClick: mouseDown }, - op.label + { ref: ref, key: 'option-' + op.value, + className: optionClass, + onMouseEnter: mouseEnter, + onMouseLeave: mouseLeave, + onMouseDown: mouseDown, + onClick: mouseDown }, + React.createElement('i', { className: op.iconClass }), + ' ', + op.create ? 'Add ' + op.label + ' ?' : op.label ); } }, this); diff --git a/package.json b/package.json index 6a3320a107..842df3aa68 100644 --- a/package.json +++ b/package.json @@ -14,11 +14,15 @@ "react-input-autosize": "^0.4.3" }, "devDependencies": { + "babel-core": "^5.4.7", "babel-eslint": "^3.1.1", "babel-jest": "^5.2.0", + "babel-plugin-object-assign": "^1.1.0", + "del": "^1.2.0", "eslint": "^0.22.1", "eslint-plugin-react": "^2.2.0", "gulp": "^3.8.11", + "gulp-babel": "^5.1.0", "jest-cli": "^0.4.2", "lessify": "^1.0.1", "react": ">=0.12.0", From 1be53a8cc64e858be61253b4bd588acf825ed3e0 Mon Sep 17 00:00:00 2001 From: Vagner Santana Date: Wed, 3 Jun 2015 11:15:03 -0300 Subject: [PATCH 3/5] removing lessify --- package.json | 6 ------ 1 file changed, 6 deletions(-) diff --git a/package.json b/package.json index 842df3aa68..97eff42e74 100644 --- a/package.json +++ b/package.json @@ -24,18 +24,12 @@ "gulp": "^3.8.11", "gulp-babel": "^5.1.0", "jest-cli": "^0.4.2", - "lessify": "^1.0.1", "react": ">=0.12.0", "react-component-gulp-tasks": "^0.7.0" }, "peerDependencies": { "react": ">=0.12.0" }, - "browserify": { - "transform": [ - "lessify" - ] - }, "browserify-shim": { "classnames": "global:classNames", "react": "global:React", From e0568789a08f2de48890f1cc889b7f046654afb0 Mon Sep 17 00:00:00 2001 From: Vagner Santana Date: Wed, 3 Jun 2015 11:23:07 -0300 Subject: [PATCH 4/5] add placeholder icon --- src/Select.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Select.js b/src/Select.js index 745898bc83..33fb6c1642 100644 --- a/src/Select.js +++ b/src/Select.js @@ -220,6 +220,7 @@ var Select = React.createClass({ inputValue: '', filteredOptions: filteredOptions, placeholder: !this.props.multi && values.length ? values[0].label : this.props.placeholder, + placeholderIcon: !this.props.multi && values.length ? values[0].iconClass : null, focusedOption: !this.props.multi && values.length ? values[0] : filteredOptions[0] }; }, @@ -716,7 +717,7 @@ var Select = React.createClass({ } if (this.props.disabled || (!this.state.inputValue && (!this.props.multi || !value.length))) { - value.push(
{this.state.placeholder}
); + value.push(
{this.state.placeholder}
); } var loading = this.state.isLoading ?