react-select-allow-create
Advanced tools
Comparing version 1.0.0-beta11.9 to 1.0.0-beta11.11
@@ -22,3 +22,2 @@ # Changes | ||
`loadingPlaceholder` prop | ||
`autoload` changed to `minimumInput` and now controls the minimum input to load options | ||
`cacheAsyncResults` -> `cache` (new external cache support) - defaults to true | ||
@@ -25,0 +24,0 @@ |
@@ -1,2 +0,2 @@ | ||
!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var u;u="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,u.Select=e()}}(function(){return function e(u,t,s){function o(i,r){if(!t[i]){if(!u[i]){var a="function"==typeof require&&require;if(!r&&a)return a(i,!0);if(n)return n(i,!0);var l=new Error("Cannot find module '"+i+"'");throw l.code="MODULE_NOT_FOUND",l}var p=t[i]={exports:{}};u[i][0].call(p.exports,function(e){var t=u[i][1][e];return o(t?t:e)},p,p.exports,e,u,t,s)}return t[i].exports}for(var n="function"==typeof require&&require,i=0;i<s.length;i++)o(s[i]);return o}({1:[function(e,u,t){(function(t){"use strict";function s(e){return e&&e.__esModule?e:{"default":e}}function o(e){return e&&"object"!=typeof e&&(e={}),e?e:null}function n(e,u,t){e&&(e[u]=t)}function i(e,u){if(e)for(var t=u.length;t>=0;--t){var s=u.slice(0,t);if(e[s]&&(u===s||e[s].complete))return e[s]}}function r(e,u){return e&&"function"==typeof e.then?e.then(function(e){u(null,e)},function(e){u(e)}):void 0}var a=Object.assign||function(e){for(var u=1;u<arguments.length;u++){var t=arguments[u];for(var s in t)Object.prototype.hasOwnProperty.call(t,s)&&(e[s]=t[s])}return e},l="undefined"!=typeof window?window.React:"undefined"!=typeof t?t.React:null,p=s(l),d=e("./Select"),c=s(d),h=e("./utils/stripDiacritics"),f=s(h),E=0,g=p["default"].PropTypes.oneOfType([p["default"].PropTypes.string,p["default"].PropTypes.node]),F=p["default"].createClass({displayName:"Async",propTypes:{cache:p["default"].PropTypes.any,ignoreAccents:p["default"].PropTypes.bool,ignoreCase:p["default"].PropTypes.bool,isLoading:p["default"].PropTypes.bool,loadOptions:p["default"].PropTypes.func.isRequired,loadingPlaceholder:p["default"].PropTypes.string,minimumInput:p["default"].PropTypes.number,noResultsText:p["default"].PropTypes.string,placeholder:g,searchPromptText:p["default"].PropTypes.string,searchingText:p["default"].PropTypes.string},getDefaultProps:function(){return{cache:!0,ignoreAccents:!0,ignoreCase:!0,loadingPlaceholder:"Loading...",minimumInput:0,searchingText:"Searching...",searchPromptText:"Type to search"}},getInitialState:function(){return{cache:o(this.props.cache),isLoading:!1,options:[]}},componentWillMount:function(){this._lastInput=""},componentDidMount:function(){this.loadOptions("")},componentWillReceiveProps:function(e){e.cache!==this.props.cache&&this.setState({cache:o(e.cache)})},focus:function(){this.refs.select.focus()},resetState:function(){this._currentRequestId=-1,this.setState({isLoading:!1,options:[]})},getResponseHandler:function(e){var u=this,t=this._currentRequestId=E++;return function(s,o){if(s)throw s;u.isMounted()&&(n(u.state.cache,e,o),t===u._currentRequestId&&u.setState({isLoading:!1,options:o&&o.options||[]}))}},loadOptions:function(e){if(this.props.ignoreAccents&&(e=(0,f["default"])(e)),this.props.ignoreCase&&(e=e.toLowerCase()),this._lastInput=e,e.length<this.props.minimumInput)return this.resetState();var u=i(this.state.cache,e);if(u)return this.setState({options:u.options});this.setState({isLoading:!0});var t=this.getResponseHandler(e);return r(this.props.loadOptions(e,t),t)},render:function(){var e=this.props.noResultsText,u=this.state,t=u.isLoading,s=u.options;this.props.isLoading&&(t=!0);var o=t?this.props.loadingPlaceholder:this.props.placeholder;return s.length||(this._lastInput.length<this.props.minimumInput&&(e=this.props.searchPromptText),t&&(e=this.props.searchingText)),p["default"].createElement(c["default"],a({},this.props,{ref:"select",isLoading:t,noResultsText:e,onInputChange:this.loadOptions,options:s,placeholder:o}))}});u.exports=F}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{"./Select":3,"./utils/stripDiacritics":5}],2:[function(e,u,t){(function(e){"use strict";function t(e){return e&&e.__esModule?e:{"default":e}}var s="undefined"!=typeof window?window.React:"undefined"!=typeof e?e.React:null,o=t(s),n="undefined"!=typeof window?window.classNames:"undefined"!=typeof e?e.classNames:null,i=t(n),r=o["default"].createClass({displayName:"Option",propTypes:{children:o["default"].PropTypes.node,className:o["default"].PropTypes.string,isDisabled:o["default"].PropTypes.bool,isFocused:o["default"].PropTypes.bool,isSelected:o["default"].PropTypes.bool,onFocus:o["default"].PropTypes.func,onSelect:o["default"].PropTypes.func,onUnfocus:o["default"].PropTypes.func,option:o["default"].PropTypes.object.isRequired},blockEvent:function(e){e.preventDefault(),e.stopPropagation(),"A"===e.target.tagName&&"href"in e.target&&(e.target.target?window.open(e.target.href,e.target.target):window.location.href=e.target.href)},handleMouseDown:function(e){e.preventDefault(),e.stopPropagation(),this.props.onSelect(this.props.option,e)},handleMouseEnter:function(e){this.onFocus(e)},handleMouseMove:function(e){this.onFocus(e)},handleTouchEnd:function(e){this.dragging||this.handleMouseDown(e)},handleTouchMove:function(e){this.dragging=!0},handleTouchStart:function(e){this.dragging=!1},onFocus:function(e){this.props.isFocused||this.props.onFocus(this.props.option,e)},render:function(){var e=this.props.option,u=(0,i["default"])(this.props.className,e.className);return e.disabled?o["default"].createElement("div",{className:u,onMouseDown:this.blockEvent,onClick:this.blockEvent},this.props.children):o["default"].createElement("div",{className:u,style:e.style,onMouseDown:this.handleMouseDown,onMouseEnter:this.handleMouseEnter,onMouseMove:this.handleMouseMove,onTouchStart:this.handleTouchStart,onTouchMove:this.handleTouchMove,onTouchEnd:this.handleTouchEnd,title:e.title},this.props.children)}});u.exports=r}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{}],3:[function(e,u,t){(function(s){"use strict";function o(e){return e&&e.__esModule?e:{"default":e}}function n(e){return"object"==typeof e?JSON.stringify(e):e}Object.defineProperty(t,"__esModule",{value:!0});var i=Object.assign||function(e){for(var u=1;u<arguments.length;u++){var t=arguments[u];for(var s in t)Object.prototype.hasOwnProperty.call(t,s)&&(e[s]=t[s])}return e},r="undefined"!=typeof window?window.React:"undefined"!=typeof s?s.React:null,a=o(r),l="undefined"!=typeof window?window.ReactDOM:"undefined"!=typeof s?s.ReactDOM:null,p=o(l),d="undefined"!=typeof window?window.AutosizeInput:"undefined"!=typeof s?s.AutosizeInput:null,c=o(d),h="undefined"!=typeof window?window.classNames:"undefined"!=typeof s?s.classNames:null,f=o(h),E=e("./utils/stripDiacritics"),g=o(E),F=e("./Async"),b=o(F),y=e("./Option"),A=o(y),m=e("./Value"),v=o(m),C=a["default"].PropTypes.oneOfType([a["default"].PropTypes.string,a["default"].PropTypes.node]),D=a["default"].createClass({displayName:"Select",propTypes:{addLabelText:a["default"].PropTypes.string,autosize:a["default"].PropTypes.bool,allowCreate:a["default"].PropTypes.bool,autoBlur:a["default"].PropTypes.bool,autofocus:a["default"].PropTypes.bool,backspaceRemoves:a["default"].PropTypes.bool,className:a["default"].PropTypes.string,clearAllText:C,clearValueText:C,clearable:a["default"].PropTypes.bool,delimiter:a["default"].PropTypes.string,disabled:a["default"].PropTypes.bool,escapeClearsValue:a["default"].PropTypes.bool,filterOption:a["default"].PropTypes.func,filterOptions:a["default"].PropTypes.any,focusedInputValue:a["default"].PropTypes.func,ignoreAccents:a["default"].PropTypes.bool,ignoreCase:a["default"].PropTypes.bool,inputProps:a["default"].PropTypes.object,isLoading:a["default"].PropTypes.bool,joinValues:a["default"].PropTypes.bool,labelKey:a["default"].PropTypes.string,matchPos:a["default"].PropTypes.string,matchProp:a["default"].PropTypes.string,menuBuffer:a["default"].PropTypes.number,menuContainerStyle:a["default"].PropTypes.object,menuStyle:a["default"].PropTypes.object,multi:a["default"].PropTypes.bool,name:a["default"].PropTypes.string,newOptionCreator:a["default"].PropTypes.func,noResultsText:C,onBlur:a["default"].PropTypes.func,onBlurResetsInput:a["default"].PropTypes.bool,onChange:a["default"].PropTypes.func,onClose:a["default"].PropTypes.func,onFocus:a["default"].PropTypes.func,onInputChange:a["default"].PropTypes.func,onMenuScrollToBottom:a["default"].PropTypes.func,onOpen:a["default"].PropTypes.func,onValueClick:a["default"].PropTypes.func,optionComponent:a["default"].PropTypes.func,optionRenderer:a["default"].PropTypes.func,options:a["default"].PropTypes.array,placeholder:C,required:a["default"].PropTypes.bool,scrollMenuIntoView:a["default"].PropTypes.bool,searchable:a["default"].PropTypes.bool,simpleValue:a["default"].PropTypes.bool,style:a["default"].PropTypes.object,tabIndex:a["default"].PropTypes.string,value:a["default"].PropTypes.any,valueComponent:a["default"].PropTypes.func,valueKey:a["default"].PropTypes.string,valueRenderer:a["default"].PropTypes.func,wrapperStyle:a["default"].PropTypes.object},statics:{Async:b["default"]},getDefaultProps:function(){return{addLabelText:'Add "{label}"?',autosize:!0,allowCreate:!1,backspaceRemoves:!0,clearable:!0,clearAllText:"Clear all",clearValueText:"Clear value",delimiter:",",disabled:!1,escapeClearsValue:!0,filterOptions:!0,ignoreAccents:!0,ignoreCase:!0,inputProps:{},isLoading:!1,joinValues:!1,labelKey:"label",matchPos:"any",matchProp:"any",menuBuffer:0,multi:!1,noResultsText:"No results found",onBlurResetsInput:!0,optionComponent:A["default"],placeholder:"Select...",required:!1,scrollMenuIntoView:!0,searchable:!0,simpleValue:!1,valueComponent:v["default"],valueKey:"value"}},getInitialState:function(){return{inputValue:"",isFocused:!1,isLoading:!1,isOpen:!1,isPseudoFocused:!1,required:this.props.required&&this.handleRequired(this.props.value,this.props.multi)}},componentDidMount:function(){this.props.autofocus&&this.focus()},componentWillReceiveProps:function(e){this.props.value!==e.value&&e.required&&this.setState({required:this.handleRequired(e.value,e.multi)})},componentWillUpdate:function(e,u){if(u.isOpen!==this.state.isOpen){var t=u.isOpen?e.onOpen:e.onClose;t&&t()}},componentDidUpdate:function(e,u){if(this.refs.menu&&this.refs.focused&&this.state.isOpen&&!this.hasScrolledToOption){var t=p["default"].findDOMNode(this.refs.focused),s=p["default"].findDOMNode(this.refs.menu);s.scrollTop=t.offsetTop,this.hasScrolledToOption=!0}else this.state.isOpen||(this.hasScrolledToOption=!1);if(u.inputValue!==this.state.inputValue&&this.props.onInputChange&&this.props.onInputChange(this.state.inputValue),this._scrollToFocusedOptionOnUpdate&&this.refs.focused&&this.refs.menu){this._scrollToFocusedOptionOnUpdate=!1;var o=p["default"].findDOMNode(this.refs.focused),n=p["default"].findDOMNode(this.refs.menu),i=o.getBoundingClientRect(),r=n.getBoundingClientRect();(i.bottom>r.bottom||i.top<r.top)&&(n.scrollTop=o.offsetTop+o.clientHeight-n.offsetHeight)}if(this.props.scrollMenuIntoView&&this.refs.menuContainer){var a=this.refs.menuContainer.getBoundingClientRect();window.innerHeight<a.bottom+this.props.menuBuffer&&window.scrollTo(0,window.scrollY+a.bottom+this.props.menuBuffer-window.innerHeight)}e.disabled!==this.props.disabled&&this.setState({isFocused:!1})},focus:function(){this.refs.input&&this.refs.input.focus()},blurInput:function(){this.refs.input&&this.refs.input.blur()},handleTouchMove:function(e){this.dragging=!0},handleTouchStart:function(e){this.dragging=!1},handleTouchEnd:function(e){this.dragging||this.handleMouseDown(e)},handleTouchEndClearValue:function(e){this.dragging||this.clearValue(e)},handleMouseDown:function(e){return this.props.disabled||"mousedown"===e.type&&0!==e.button?void 0:(this.state.isOpen||e.preventDefault(),e.stopPropagation(),this.props.searchable?void(this.state.isFocused?this.setState({isOpen:!0,isPseudoFocused:!1}):(this._openAfterFocus=!0,this.focus())):(this.focus(),this.setState({isOpen:!this.state.isOpen})))},handleMouseDownOnArrow:function(e){this.props.disabled||"mousedown"===e.type&&0!==e.button||this.state.isOpen&&(e.stopPropagation(),e.preventDefault(),this.closeMenu())},handleMouseDownOnMenu:function(e){this.props.disabled||"mousedown"===e.type&&0!==e.button||(e.stopPropagation(),e.preventDefault(),this._openAfterFocus=!0,this.focus())},closeMenu:function(){this.setState({isOpen:!1,isPseudoFocused:this.state.isFocused&&!this.props.multi,inputValue:""}),this.hasScrolledToOption=!1},handleInputFocus:function(e){var u=this.state.isOpen||this._openAfterFocus;this.props.onFocus&&this.props.onFocus(e);var t={isFocused:!0,isOpen:u};this.props.focusedInputValue&&!this.props.multi&&(t.inputValue=this.props.focusedInputValue(this.props.value)),this.setState(t),this._openAfterFocus=!1},handleInputBlur:function(e){if(!this.refs.menu||!document.activeElement.isEqualNode(this.refs.menu)){this.props.onBlur&&this.props.onBlur(e);var u={isFocused:!1,isOpen:!1,isPseudoFocused:!1};this.props.onBlurResetsInput&&(u.inputValue=""),this.setState(u)}},handleInputChange:function(e){this.setState({isOpen:!0,isPseudoFocused:!1,inputValue:e.target.value})},handleKeyDown:function(e){if(!this.props.disabled){switch(e.keyCode){case 8:return void(!this.state.inputValue&&this.props.backspaceRemoves&&(e.preventDefault(),this.popValue()));case 9:if(e.shiftKey||!this.state.isOpen)return;return void this.selectFocusedOption();case 13:if(!this.state.isOpen)return;e.stopPropagation(),this.selectFocusedOption();break;case 27:this.state.isOpen?this.closeMenu():this.props.clearable&&this.props.escapeClearsValue&&this.clearValue(e);break;case 38:this.focusPreviousOption();break;case 40:this.focusNextOption();break;default:return}e.preventDefault()}},handleValueClick:function(e,u){this.props.onValueClick&&this.props.onValueClick(e,u)},handleMenuScroll:function(e){if(this.props.onMenuScrollToBottom){var u=e.target;u.scrollHeight>u.offsetHeight&&!(u.scrollHeight-u.offsetHeight-u.scrollTop)&&this.props.onMenuScrollToBottom()}},handleRequired:function(e,u){return e?u?0===e.length:0===Object.keys(e).length:!0},getOptionLabel:function(e){return e[this.props.labelKey]},getValueArray:function(){var e=this.props.value;if(this.props.multi){if("string"==typeof e&&(e=e.split(this.props.delimiter)),!Array.isArray(e)){if(null===e||void 0===e)return[];e=[e]}return e.map(this.expandValue).filter(function(e){return e})}var u=this.expandValue(e);return u?[u]:[]},expandValue:function(e){if("string"!=typeof e&&"number"!=typeof e)return e;var u=this.props,t=u.options,s=u.valueKey;if(t)for(var o=0;o<t.length;o++)if(t[o][s]===e)return t[o]},setValue:function(e){var u=this;if(this.props.autoBlur&&this.blurInput(),this.props.onChange){if(this.props.required){var t=this.handleRequired(e,this.props.multi);this.setState({required:t})}this.props.simpleValue&&e&&(e=this.props.multi?e.map(function(e){return e[u.props.valueKey]}).join(this.props.delimiter):e[this.props.valueKey]),this.props.onChange(e)}},selectValue:function(e){this.hasScrolledToOption=!1,this.props.multi?(this.addValue(e),this.setState({inputValue:""})):(this.setValue(e),this.setState({isOpen:!1,inputValue:"",isPseudoFocused:this.state.isFocused}))},addValue:function(e){var u=this.getValueArray();this.setValue(u.concat(e))},popValue:function(){var e=this.getValueArray();e.length&&e[e.length-1].clearableValue!==!1&&this.setValue(e.slice(0,e.length-1))},removeValue:function(e){var u=this.getValueArray();this.setValue(u.filter(function(u){return u!==e})),this.focus()},clearValue:function(e){e&&"mousedown"===e.type&&0!==e.button||(e.stopPropagation(),e.preventDefault(),this.setValue(null),this.setState({isOpen:!1,inputValue:""},this.focus))},focusOption:function(e){this.setState({focusedOption:e})},focusNextOption:function(){this.focusAdjacentOption("next")},focusPreviousOption:function(){this.focusAdjacentOption("previous")},focusAdjacentOption:function(e){var u=this._visibleOptions.filter(function(e){return!e.disabled});if(this._scrollToFocusedOptionOnUpdate=!0,!this.state.isOpen)return void this.setState({isOpen:!0,inputValue:"",focusedOption:this._focusedOption||u["next"===e?0:u.length-1]});if(u.length){for(var t=-1,s=0;s<u.length;s++)if(this._focusedOption===u[s]){t=s;break}var o=u[0];"next"===e&&t>-1&&t<u.length-1?o=u[t+1]:"previous"===e&&(o=t>0?u[t-1]:u[u.length-1]),this.setState({focusedOption:o})}},selectFocusedOption:function(){var e=this.filterOptions(null),u=this._focusedOption&&e.length>0;return this.props.allowCreate&&!u?this.selectValue(this.state.inputValue):this._focusedOption?this.selectValue(this._focusedOption):void 0},renderLoading:function(){return this.props.isLoading?a["default"].createElement("span",{className:"Select-loading-zone","aria-hidden":"true"},a["default"].createElement("span",{className:"Select-loading"})):void 0},renderValue:function(e,u){var t=this,s=this.props.valueRenderer||this.getOptionLabel,o=this.props.valueComponent;if(!e.length)return this.state.inputValue?null:a["default"].createElement("div",{className:"Select-placeholder"},this.props.placeholder);var n=this.props.onValueClick?this.handleValueClick:null;return this.props.multi?e.map(function(e,u){return a["default"].createElement(o,{disabled:t.props.disabled||e.clearableValue===!1,key:"value-"+u+"-"+e[t.props.valueKey],onClick:n,onRemove:t.removeValue,value:e},s(e))}):this.state.inputValue?void 0:(u&&(n=null),a["default"].createElement(o,{disabled:this.props.disabled,onClick:n,value:e[0]},s(e[0])))},renderInput:function(e){var u=(0,f["default"])("Select-input",this.props.inputProps.className);return this.props.disabled||!this.props.searchable?a["default"].createElement("div",i({},this.props.inputProps,{className:u,tabIndex:this.props.tabIndex||0,onBlur:this.handleInputBlur,onFocus:this.handleInputFocus,ref:"input",style:{border:0,width:1,display:"inline-block"}})):this.props.autosize?a["default"].createElement(c["default"],i({},this.props.inputProps,{className:u,tabIndex:this.props.tabIndex,onBlur:this.handleInputBlur,onChange:this.handleInputChange,onFocus:this.handleInputFocus,minWidth:"5",ref:"input",required:this.state.required,value:this.state.inputValue})):a["default"].createElement("div",{className:u},a["default"].createElement("input",i({},this.props.inputProps,{tabIndex:this.props.tabIndex,onBlur:this.handleInputBlur,onChange:this.handleInputChange,onFocus:this.handleInputFocus,ref:"input",required:this.state.required,value:this.state.inputValue})))},renderClear:function(){return!this.props.clearable||!this.props.value||this.props.multi&&!this.props.value.length||this.props.disabled||this.props.isLoading?void 0:a["default"].createElement("span",{className:"Select-clear-zone",title:this.props.multi?this.props.clearAllText:this.props.clearValueText,"aria-label":this.props.multi?this.props.clearAllText:this.props.clearValueText,onMouseDown:this.clearValue,onTouchStart:this.handleTouchStart,onTouchMove:this.handleTouchMove,onTouchEnd:this.handleTouchEndClearValue},a["default"].createElement("span",{className:"Select-clear",dangerouslySetInnerHTML:{__html:"×"}}))},renderArrow:function(){return a["default"].createElement("span",{className:"Select-arrow-zone",onMouseDown:this.handleMouseDownOnArrow},a["default"].createElement("span",{className:"Select-arrow",onMouseDown:this.handleMouseDownOnArrow}))},filterOptions:function(e){var u=this,t=this.state.inputValue,s=this.props.options||[];return"function"==typeof this.props.filterOptions?this.props.filterOptions.call(this,s,t,e):this.props.filterOptions?(this.props.ignoreAccents&&(t=(0,g["default"])(t)),this.props.ignoreCase&&(t=t.toLowerCase()),e&&(e=e.map(function(e){return e[u.props.valueKey]})),s.filter(function(s){if(e&&e.indexOf(s[u.props.valueKey])>-1)return!1;if(u.props.filterOption)return u.props.filterOption.call(u,s,t);if(!t)return!0;var o=String(s[u.props.valueKey]),n=String(s[u.props.labelKey]);return u.props.ignoreAccents&&("label"!==u.props.matchProp&&(o=(0,g["default"])(o)),"value"!==u.props.matchProp&&(n=(0,g["default"])(n))),u.props.ignoreCase&&("label"!==u.props.matchProp&&(o=o.toLowerCase()),"value"!==u.props.matchProp&&(n=n.toLowerCase())),"start"===u.props.matchPos?"label"!==u.props.matchProp&&o.substr(0,t.length)===t||"value"!==u.props.matchProp&&n.substr(0,t.length)===t:"label"!==u.props.matchProp&&o.indexOf(t)>=0||"value"!==u.props.matchProp&&n.indexOf(t)>=0})):s},renderMenu:function(e,u,t){var s=this;if(!e||!e.length)return this.props.noResultsText?a["default"].createElement("div",{className:"Select-noresults"},this.props.noResultsText):null;var o=function(){var o=s.props.optionComponent,n=s.props.optionRenderer||s.getOptionLabel;return{v:e.map(function(e,i){var r=u&&u.indexOf(e)>-1,l=e===t,p=l?"focused":null,d=(0,f["default"])({"Select-option":!0,"is-selected":r,"is-focused":l,"is-disabled":e.disabled});return a["default"].createElement(o,{className:d,isDisabled:e.disabled,isFocused:l,key:"option-"+i+"-"+e[s.props.valueKey],onSelect:s.selectValue,onFocus:s.focusOption,option:e,isSelected:r,ref:p},n(e))})}}();return"object"==typeof o?o.v:void 0},renderHiddenField:function(e){var u=this;if(this.props.name){if(this.props.joinValues){var t=e.map(function(e){return n(e[u.props.valueKey])}).join(this.props.delimiter);return a["default"].createElement("input",{type:"hidden",ref:"value",name:this.props.name,value:t,disabled:this.props.disabled})}return e.map(function(e,t){return a["default"].createElement("input",{key:"hidden."+t,type:"hidden",ref:"value"+t,name:u.props.name,value:n(e[u.props.valueKey]),disabled:u.props.disabled})})}},getFocusableOption:function(e){var u=this._visibleOptions;if(u.length){var t=this.state.focusedOption||e;if(t&&u.indexOf(t)>-1)return t;for(var s=0;s<u.length;s++)if(!u[s].disabled)return u[s]}},render:function(){var e=this.getValueArray(),u=this._visibleOptions=this.filterOptions(this.props.multi?e:null),t=this.state.isOpen;this.props.multi&&!u.length&&e.length&&!this.state.inputValue&&(t=!1);var s=this._focusedOption=this.getFocusableOption(e[0]),o=(0,f["default"])("Select",this.props.className,{"Select--multi":this.props.multi,"is-disabled":this.props.disabled,"is-focused":this.state.isFocused,"is-loading":this.props.isLoading,"is-open":t,"is-pseudo-focused":this.state.isPseudoFocused,"is-searchable":this.props.searchable,"has-value":e.length});return a["default"].createElement("div",{ref:"wrapper",className:o,style:this.props.wrapperStyle},this.renderHiddenField(e),a["default"].createElement("div",{ref:"control",className:"Select-control",style:this.props.style,onKeyDown:this.handleKeyDown,onMouseDown:this.handleMouseDown,onTouchEnd:this.handleTouchEnd,onTouchStart:this.handleTouchStart,onTouchMove:this.handleTouchMove},this.renderValue(e,t),this.renderInput(e),this.renderLoading(),this.renderClear(),this.renderArrow()),t?a["default"].createElement("div",{ref:"menuContainer",className:"Select-menu-outer",style:this.props.menuContainerStyle},a["default"].createElement("div",{ref:"menu",className:"Select-menu",style:this.props.menuStyle,onScroll:this.handleMenuScroll,onMouseDown:this.handleMouseDownOnMenu},this.renderMenu(u,this.props.multi?null:e,s))):null)}});t["default"]=D,u.exports=t["default"]}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{"./Async":1,"./Option":2,"./Value":4,"./utils/stripDiacritics":5}],4:[function(e,u,t){(function(e){"use strict";function t(e){return e&&e.__esModule?e:{"default":e}}var s="undefined"!=typeof window?window.React:"undefined"!=typeof e?e.React:null,o=t(s),n="undefined"!=typeof window?window.classNames:"undefined"!=typeof e?e.classNames:null,i=t(n),r=o["default"].createClass({displayName:"Value",propTypes:{children:o["default"].PropTypes.node,disabled:o["default"].PropTypes.bool,onClick:o["default"].PropTypes.func,onRemove:o["default"].PropTypes.func,value:o["default"].PropTypes.object.isRequired},handleMouseDown:function(e){return"mousedown"!==e.type||0===e.button?this.props.onClick?(e.stopPropagation(),void this.props.onClick(this.props.value,e)):void(this.props.value.href&&e.stopPropagation()):void 0},onRemove:function(e){e.preventDefault(),e.stopPropagation(),this.props.onRemove(this.props.value)},handleTouchEndRemove:function(e){this.dragging||this.onRemove(e)},handleTouchMove:function(e){this.dragging=!0},handleTouchStart:function(e){this.dragging=!1},renderRemoveIcon:function(){return!this.props.disabled&&this.props.onRemove?o["default"].createElement("span",{className:"Select-value-icon",onMouseDown:this.onRemove,onTouchEnd:this.handleTouchEndRemove,onTouchStart:this.handleTouchStart,onTouchMove:this.handleTouchMove},"×"):void 0},renderLabel:function(){var e="Select-value-label";return this.props.onClick||this.props.value.href?o["default"].createElement("a",{className:e,href:this.props.value.href,target:this.props.value.target,onMouseDown:this.handleMouseDown,onTouchEnd:this.handleMouseDown},this.props.children):o["default"].createElement("span",{className:e},this.props.children)},render:function(){return o["default"].createElement("div",{className:(0,i["default"])("Select-value",this.props.value.className),style:this.props.value.style,title:this.props.value.title},this.renderRemoveIcon(),this.renderLabel())}});u.exports=r}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{}],5:[function(e,u,t){"use strict";var s=[{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}];u.exports=function(e){for(var u=0;u<s.length;u++)e=e.replace(s[u].letters,s[u].base);return e}},{}]},{},[3])(3)}); | ||
!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var t;t="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,t.Select=e()}}(function(){return function e(t,u,n){function s(i,a){if(!u[i]){if(!t[i]){var r="function"==typeof require&&require;if(!a&&r)return r(i,!0);if(o)return o(i,!0);var l=new Error("Cannot find module '"+i+"'");throw l.code="MODULE_NOT_FOUND",l}var p=u[i]={exports:{}};t[i][0].call(p.exports,function(e){var u=t[i][1][e];return s(u?u:e)},p,p.exports,e,t,u,n)}return u[i].exports}for(var o="function"==typeof require&&require,i=0;i<n.length;i++)s(n[i]);return s}({1:[function(e,t,u){(function(n){"use strict";function s(e){return e&&e.__esModule?e:{"default":e}}function o(e,t,u){return t in e?Object.defineProperty(e,t,{value:u,enumerable:!0,configurable:!0,writable:!0}):e[t]=u,e}function i(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function a(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}function r(e){return f["default"].createElement(E["default"],e)}Object.defineProperty(u,"__esModule",{value:!0});var l=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var u=arguments[t];for(var n in u)Object.prototype.hasOwnProperty.call(u,n)&&(e[n]=u[n])}return e},p=function(){function e(e,t){for(var u=0;u<t.length;u++){var n=t[u];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(e,n.key,n)}}return function(t,u,n){return u&&e(t.prototype,u),n&&e(t,n),t}}(),d=function(e,t,u){for(var n=!0;n;){var s=e,o=t,i=u;n=!1,null===s&&(s=Function.prototype);var a=Object.getOwnPropertyDescriptor(s,o);if(void 0!==a){if("value"in a)return a.value;var r=a.get;if(void 0===r)return;return r.call(i)}var l=Object.getPrototypeOf(s);if(null===l)return;e=l,t=o,u=i,n=!0,a=l=void 0}},c="undefined"!=typeof window?window.React:"undefined"!=typeof n?n.React:null,f=s(c),h=e("./Select"),E=s(h),y=e("./utils/stripDiacritics"),g=s(y),b={autoload:f["default"].PropTypes.bool.isRequired,cache:f["default"].PropTypes.any,children:f["default"].PropTypes.func.isRequired,ignoreAccents:f["default"].PropTypes.bool,ignoreCase:f["default"].PropTypes.bool,loadingPlaceholder:f["default"].PropTypes.oneOfType([f["default"].PropTypes.string,f["default"].PropTypes.node]),loadOptions:f["default"].PropTypes.func.isRequired,options:c.PropTypes.array.isRequired,placeholder:f["default"].PropTypes.oneOfType([f["default"].PropTypes.string,f["default"].PropTypes.node]),noResultsText:f["default"].PropTypes.oneOfType([f["default"].PropTypes.string,f["default"].PropTypes.node]),searchPromptText:f["default"].PropTypes.oneOfType([f["default"].PropTypes.string,f["default"].PropTypes.node]),onInputChange:f["default"].PropTypes.func,value:f["default"].PropTypes.any},v={autoload:!0,cache:{},children:r,ignoreAccents:!0,ignoreCase:!0,loadingPlaceholder:"Loading...",options:[],searchPromptText:"Type to search"},m=function(e){function t(e,u){i(this,t),d(Object.getPrototypeOf(t.prototype),"constructor",this).call(this,e,u),this.state={isLoading:!1,options:e.options},this._onInputChange=this._onInputChange.bind(this)}return a(t,e),p(t,[{key:"componentDidMount",value:function(){var e=this.props.autoload;e&&this.loadOptions("")}},{key:"componentWillUpdate",value:function(e,t){var u=this,n=["options"];n.forEach(function(t){u.props[t]!==e[t]&&u.setState(o({},t,e[t]))})}},{key:"clearOptions",value:function(){this.setState({options:[]})}},{key:"loadOptions",value:function u(e){var t=this,n=this.props,s=n.cache,u=n.loadOptions;if(s&&s.hasOwnProperty(e))return void this.setState({options:s[e]});var o=function a(u,n){if(a===t._callback){t._callback=null;var o=n&&n.options||[];s&&(s[e]=o),t.setState({isLoading:!1,options:o})}};this._callback=o;var i=u(e,o);return i&&i.then(function(e){return o(null,e)},function(e){return o(e)}),this._callback&&!this.state.isLoading&&this.setState({isLoading:!0}),e}},{key:"_onInputChange",value:function(e){var t=this.props,u=t.ignoreAccents,n=t.ignoreCase,s=t.onInputChange;return u&&(e=(0,g["default"])(e)),n&&(e=e.toLowerCase()),s&&s(e),this.loadOptions(e)}},{key:"inputValue",value:function(){return this.select?this.select.state.inputValue:""}},{key:"noResultsText",value:function n(){var e=this.props,t=e.loadingPlaceholder,n=e.noResultsText,u=e.searchPromptText,s=this.state.isLoading,o=this.inputValue();return s?t:o&&n?n:u}},{key:"render",value:function(){var e=this,t=this.props,u=t.children,n=t.loadingPlaceholder,s=t.placeholder,o=this.state,i=o.isLoading,a=o.options,r={noResultsText:this.noResultsText(),placeholder:i?n:s,options:i&&n?[]:a,ref:function(t){return e.select=t},onChange:function(t){e.props.value&&t.length>e.props.value.length&&e.clearOptions(),e.props.onChange(t)}};return u(l({},this.props,r,{isLoading:i,onInputChange:this._onInputChange}))}}]),t}(c.Component);u["default"]=m,m.propTypes=b,m.defaultProps=v,t.exports=u["default"]}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{"./Select":5,"./utils/stripDiacritics":10}],2:[function(e,t,u){(function(u){"use strict";function n(e){return e&&e.__esModule?e:{"default":e}}var s=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var u=arguments[t];for(var n in u)Object.prototype.hasOwnProperty.call(u,n)&&(e[n]=u[n])}return e},o="undefined"!=typeof window?window.React:"undefined"!=typeof u?u.React:null,i=n(o),a=e("./Select"),r=n(a),l=i["default"].createClass({displayName:"AsyncCreatableSelect",render:function(){var e=this;return i["default"].createElement(r["default"].Async,this.props,function(t){return i["default"].createElement(r["default"].Creatable,e.props,function(e){return i["default"].createElement(r["default"],s({},t,e,{onInputChange:function(u){return e.onInputChange(u),t.onInputChange(u)}}))})})}});t.exports=l}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{"./Select":5}],3:[function(e,t,u){(function(u){"use strict";function n(e){return e&&e.__esModule?e:{"default":e}}function s(e,t){var u={};for(var n in e)t.indexOf(n)>=0||Object.prototype.hasOwnProperty.call(e,n)&&(u[n]=e[n]);return u}function o(e){return f["default"].createElement(E["default"],e)}function i(e){var t=e.option,u=e.options,n=e.labelKey,s=e.valueKey;return 0===u.filter(function(e){return e[n]===t[n]||e[s]===t[s]}).length}function a(e){var t=e.label;return!!t}function r(e){var t=e.label,u=e.labelKey,n=e.valueKey,s={};return s[n]=t,s[u]=t,s.className="Select-create-option-placeholder",s}function l(e){return'Create option "'+e+'"'}function p(e){var t=e.keyCode;switch(t){case 9:case 13:case 188:return!0}return!1}var d=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var u=arguments[t];for(var n in u)Object.prototype.hasOwnProperty.call(u,n)&&(e[n]=u[n])}return e},c="undefined"!=typeof window?window.React:"undefined"!=typeof u?u.React:null,f=n(c),h=e("./Select"),E=n(h),y=e("./utils/defaultFilterOptions"),g=n(y),b=e("./utils/defaultMenuRenderer"),v=n(b),m=f["default"].createClass({displayName:"CreatableSelect",propTypes:{children:f["default"].PropTypes.func,filterOptions:f["default"].PropTypes.any,isOptionUnique:f["default"].PropTypes.func,isValidNewOption:f["default"].PropTypes.func,menuRenderer:f["default"].PropTypes.any,newOptionCreator:f["default"].PropTypes.func,options:f["default"].PropTypes.array,promptTextCreator:f["default"].PropTypes.func,shouldKeyDownEventCreateNewOption:f["default"].PropTypes.func},statics:{isOptionUnique:i,isValidNewOption:a,newOptionCreator:r,promptTextCreator:l,shouldKeyDownEventCreateNewOption:p},getDefaultProps:function(){return{filterOptions:g["default"],isOptionUnique:i,isValidNewOption:a,menuRenderer:v["default"],newOptionCreator:r,promptTextCreator:l,shouldKeyDownEventCreateNewOption:p}},createNewOption:function(){var e=this.props,t=e.isValidNewOption,u=e.newOptionCreator,n=e.options,s=void 0===n?[]:n;e.shouldKeyDownEventCreateNewOption;if(t({label:this.inputValue})){var o=u({label:this.inputValue,labelKey:this.labelKey,valueKey:this.valueKey}),i=this.isOptionUnique({option:o});i&&(s.unshift(o),this.select.selectValue(o))}},filterOptions:function F(){var e=this.props,F=e.filterOptions,t=e.isValidNewOption,u=(e.options,e.promptTextCreator),n=arguments[2]||[],s=F.apply(void 0,arguments)||[];if(t({label:this.inputValue})){var o=this.props.newOptionCreator,i=o({label:this.inputValue,labelKey:this.labelKey,valueKey:this.valueKey}),a=this.isOptionUnique({option:i,options:n.concat(s)});if(a){var r=u(this.inputValue);this._createPlaceholderOption=o({label:r,labelKey:this.labelKey,valueKey:this.valueKey}),s.unshift(this._createPlaceholderOption)}}return s},isOptionUnique:function C(e){var t=e.option,u=e.options,C=this.props.isOptionUnique;return u=u||this.select.filterOptions(),C({labelKey:this.labelKey,option:t,options:u,valueKey:this.valueKey})},menuRenderer:function O(e){var O=this.props.menuRenderer;return O(d({},e,{onSelect:this.onOptionSelect}))},onInputChange:function(e){this.inputValue=e},onInputKeyDown:function A(e){var t=this.props,u=t.shouldKeyDownEventCreateNewOption,A=t.onInputKeyDown,n=this.select.getFocusedOption();n&&n===this._createPlaceholderOption&&u({keyCode:e.keyCode})?(this.createNewOption(),e.preventDefault()):A&&A(e)},onOptionSelect:function(e,t){e===this._createPlaceholderOption?this.createNewOption():this.select.selectValue(e)},render:function(){var e=this,t=this.props,u=t.children,n=void 0===u?o:u,i=(t.newOptionCreator,t.shouldKeyDownEventCreateNewOption,s(t,["children","newOptionCreator","shouldKeyDownEventCreateNewOption"])),a=d({},i,{allowCreate:!0,filterOptions:this.filterOptions,menuRenderer:this.menuRenderer,onInputChange:this.onInputChange,onInputKeyDown:this.onInputKeyDown,ref:function(t){e.select=t,t&&(e.labelKey=t.props.labelKey,e.valueKey=t.props.valueKey)}});return n(a)}});t.exports=m}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{"./Select":5,"./utils/defaultFilterOptions":8,"./utils/defaultMenuRenderer":9}],4:[function(e,t,u){(function(e){"use strict";function u(e){return e&&e.__esModule?e:{"default":e}}var n="undefined"!=typeof window?window.React:"undefined"!=typeof e?e.React:null,s=u(n),o="undefined"!=typeof window?window.classNames:"undefined"!=typeof e?e.classNames:null,i=u(o),a=s["default"].createClass({displayName:"Option",propTypes:{children:s["default"].PropTypes.node,className:s["default"].PropTypes.string,instancePrefix:s["default"].PropTypes.string.isRequired,isDisabled:s["default"].PropTypes.bool,isFocused:s["default"].PropTypes.bool,isSelected:s["default"].PropTypes.bool,onFocus:s["default"].PropTypes.func,onSelect:s["default"].PropTypes.func,onUnfocus:s["default"].PropTypes.func,option:s["default"].PropTypes.object.isRequired,optionIndex:s["default"].PropTypes.number},blockEvent:function(e){e.preventDefault(),e.stopPropagation(),"A"===e.target.tagName&&"href"in e.target&&(e.target.target?window.open(e.target.href,e.target.target):window.location.href=e.target.href)},handleMouseDown:function(e){e.preventDefault(),e.stopPropagation(),this.props.onSelect(this.props.option,e)},handleMouseEnter:function(e){this.onFocus(e)},handleMouseMove:function(e){this.onFocus(e)},handleTouchEnd:function(e){this.dragging||this.handleMouseDown(e)},handleTouchMove:function(e){this.dragging=!0},handleTouchStart:function(e){this.dragging=!1},onFocus:function(e){this.props.isFocused||this.props.onFocus(this.props.option,e)},render:function(){var e=this.props,t=e.option,u=e.instancePrefix,n=e.optionIndex,o=(0,i["default"])(this.props.className,t.className);return t.disabled?s["default"].createElement("div",{className:o,onMouseDown:this.blockEvent,onClick:this.blockEvent},this.props.children):s["default"].createElement("div",{className:o,style:t.style,role:"option",onMouseDown:this.handleMouseDown,onMouseEnter:this.handleMouseEnter,onMouseMove:this.handleMouseMove,onTouchStart:this.handleTouchStart,onTouchMove:this.handleTouchMove,onTouchEnd:this.handleTouchEnd,id:u+"-option-"+n,title:t.title},this.props.children)}});t.exports=a}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{}],5:[function(e,t,u){(function(n){"use strict";function s(e){return e&&e.__esModule?e:{"default":e}}function o(e,t){var u={};for(var n in e)t.indexOf(n)>=0||Object.prototype.hasOwnProperty.call(e,n)&&(u[n]=e[n]);return u}function i(e,t,u){return t in e?Object.defineProperty(e,t,{value:u,enumerable:!0,configurable:!0,writable:!0}):e[t]=u,e}function a(e){var t=typeof e;return"string"===t?e:"object"===t?JSON.stringify(e):"number"===t||"boolean"===t?String(e):""}Object.defineProperty(u,"__esModule",{value:!0});var r=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var u=arguments[t];for(var n in u)Object.prototype.hasOwnProperty.call(u,n)&&(e[n]=u[n])}return e},l="undefined"!=typeof window?window.React:"undefined"!=typeof n?n.React:null,p=s(l),d="undefined"!=typeof window?window.ReactDOM:"undefined"!=typeof n?n.ReactDOM:null,c=s(d),f="undefined"!=typeof window?window.AutosizeInput:"undefined"!=typeof n?n.AutosizeInput:null,h=s(f),E="undefined"!=typeof window?window.classNames:"undefined"!=typeof n?n.classNames:null,y=s(E),g=e("./utils/defaultArrowRenderer"),b=s(g),v=e("./utils/defaultFilterOptions"),m=s(v),F=e("./utils/defaultMenuRenderer"),C=s(F),O=e("./Async"),A=s(O),w=e("./AsyncCreatable"),P=s(w),T=e("./Creatable"),D=s(T),V=e("./Option"),S=s(V),B=e("./Value"),x=s(B),R=p["default"].PropTypes.oneOfType([p["default"].PropTypes.string,p["default"].PropTypes.node]),M=1,N=p["default"].createClass({displayName:"Select",propTypes:{addLabelText:p["default"].PropTypes.string,"aria-label":p["default"].PropTypes.string,"aria-labelledby":p["default"].PropTypes.string,arrowRenderer:p["default"].PropTypes.func,autoBlur:p["default"].PropTypes.bool,autofocus:p["default"].PropTypes.bool,autosize:p["default"].PropTypes.bool,backspaceRemoves:p["default"].PropTypes.bool,backspaceToRemoveMessage:p["default"].PropTypes.string,className:p["default"].PropTypes.string,clearAllText:R,clearValueText:R,clearable:p["default"].PropTypes.bool,delimiter:p["default"].PropTypes.string,disabled:p["default"].PropTypes.bool,escapeClearsValue:p["default"].PropTypes.bool,filterOption:p["default"].PropTypes.func,filterOptions:p["default"].PropTypes.any,focusedInputValue:p["default"].PropTypes.func,ignoreAccents:p["default"].PropTypes.bool,ignoreCase:p["default"].PropTypes.bool,inputProps:p["default"].PropTypes.object,inputRenderer:p["default"].PropTypes.func,instanceId:p["default"].PropTypes.string,isLoading:p["default"].PropTypes.bool,joinValues:p["default"].PropTypes.bool,labelKey:p["default"].PropTypes.string,matchPos:p["default"].PropTypes.string,matchProp:p["default"].PropTypes.string,menuBuffer:p["default"].PropTypes.number,menuContainerStyle:p["default"].PropTypes.object,menuRenderer:p["default"].PropTypes.func,menuStyle:p["default"].PropTypes.object,multi:p["default"].PropTypes.bool,name:p["default"].PropTypes.string,noResultsText:R,onBlur:p["default"].PropTypes.func,onBlurResetsInput:p["default"].PropTypes.bool,onChange:p["default"].PropTypes.func,onClose:p["default"].PropTypes.func,onCloseResetsInput:p["default"].PropTypes.bool,onFocus:p["default"].PropTypes.func,onInputChange:p["default"].PropTypes.func,onInputKeyDown:p["default"].PropTypes.func,onMenuScrollToBottom:p["default"].PropTypes.func,onOpen:p["default"].PropTypes.func,onValueClick:p["default"].PropTypes.func,openAfterFocus:p["default"].PropTypes.bool,openOnFocus:p["default"].PropTypes.bool,optionClassName:p["default"].PropTypes.string,optionComponent:p["default"].PropTypes.func,optionRenderer:p["default"].PropTypes.func,options:p["default"].PropTypes.array,pageSize:p["default"].PropTypes.number,placeholder:R,required:p["default"].PropTypes.bool,resetValue:p["default"].PropTypes.any,scrollMenuIntoView:p["default"].PropTypes.bool,searchable:p["default"].PropTypes.bool,simpleValue:p["default"].PropTypes.bool,style:p["default"].PropTypes.object,tabIndex:p["default"].PropTypes.string,tabSelectsValue:p["default"].PropTypes.bool,value:p["default"].PropTypes.any,valueComponent:p["default"].PropTypes.func,valueKey:p["default"].PropTypes.string,valueRenderer:p["default"].PropTypes.func,wrapperStyle:p["default"].PropTypes.object,inputValue:p["default"].PropTypes.string},statics:{Async:A["default"],AsyncCreatable:P["default"],Creatable:D["default"]},getDefaultProps:function(){return{addLabelText:'Add "{label}"?',arrowRenderer:b["default"],autosize:!0,backspaceRemoves:!0,backspaceToRemoveMessage:"Press backspace to remove {label}",clearable:!0,clearAllText:"Clear all",clearValueText:"Clear value",delimiter:",",disabled:!1,escapeClearsValue:!0,filterOptions:m["default"],ignoreAccents:!0,ignoreCase:!0,inputProps:{},isLoading:!1,joinValues:!1,labelKey:"label",matchPos:"any",matchProp:"any",menuBuffer:0,menuRenderer:C["default"],multi:!1,noResultsText:"No results found",onBlurResetsInput:!0,onCloseResetsInput:!0,openAfterFocus:!1,optionComponent:S["default"],pageSize:5,placeholder:"Select...",required:!1,scrollMenuIntoView:!0,searchable:!0,simpleValue:!1,tabSelectsValue:!0,valueComponent:x["default"],valueKey:"value",inputValue:""}},getInitialState:function(){return{inputValue:this.props.inputValue,isFocused:!1,isOpen:!1,isPseudoFocused:!1,required:!1}},componentWillMount:function(){this._instancePrefix="react-select-"+(this.props.instanceId||++M)+"-";var e=this.getValueArray(this.props.value);this.props.required&&this.setState({required:this.handleRequired(e[0],this.props.multi)})},componentDidMount:function(){this.props.autofocus&&this.focus()},componentWillReceiveProps:function(e){var t=this.getValueArray(e.value,e);e.required&&this.setState({required:this.handleRequired(t[0],e.multi)}),""!==e.inputValue&&this.setState({inputValue:e.inputValue})},componentWillUpdate:function(e,t){if(t.isOpen!==this.state.isOpen){this.toggleTouchOutsideEvent(t.isOpen);var u=t.isOpen?e.onOpen:e.onClose;u&&u()}},componentDidUpdate:function(e,t){if(this.menu&&this.focused&&this.state.isOpen&&!this.hasScrolledToOption){var u=c["default"].findDOMNode(this.focused),n=c["default"].findDOMNode(this.menu);n.scrollTop=u.offsetTop,this.hasScrolledToOption=!0}else this.state.isOpen||(this.hasScrolledToOption=!1);if(this._scrollToFocusedOptionOnUpdate&&this.focused&&this.menu){this._scrollToFocusedOptionOnUpdate=!1;var s=c["default"].findDOMNode(this.focused),o=c["default"].findDOMNode(this.menu),i=s.getBoundingClientRect(),a=o.getBoundingClientRect();(i.bottom>a.bottom||i.top<a.top)&&(o.scrollTop=s.offsetTop+s.clientHeight-o.offsetHeight)}if(this.props.scrollMenuIntoView&&this.menuContainer){var r=this.menuContainer.getBoundingClientRect();window.innerHeight<r.bottom+this.props.menuBuffer&&window.scrollBy(0,r.bottom+this.props.menuBuffer-window.innerHeight)}e.disabled!==this.props.disabled&&(this.setState({isFocused:!1}),this.closeMenu())},componentWillUnmount:function(){!document.removeEventListener&&document.detachEvent?document.detachEvent("ontouchstart",this.handleTouchOutside):document.removeEventListener("touchstart",this.handleTouchOutside)},toggleTouchOutsideEvent:function(e){e?!document.addEventListener&&document.attachEvent?document.attachEvent("ontouchstart",this.handleTouchOutside):document.addEventListener("touchstart",this.handleTouchOutside):!document.removeEventListener&&document.detachEvent?document.detachEvent("ontouchstart",this.handleTouchOutside):document.removeEventListener("touchstart",this.handleTouchOutside)},handleTouchOutside:function(e){this.wrapper&&!this.wrapper.contains(e.target)&&this.closeMenu()},focus:function(){this.input&&(this.input.focus(),this.props.openAfterFocus&&this.setState({isOpen:!0}))},blurInput:function(){this.input&&this.input.blur()},handleTouchMove:function(e){this.dragging=!0},handleTouchStart:function(e){this.dragging=!1},handleTouchEnd:function(e){this.dragging||this.handleMouseDown(e)},handleTouchEndClearValue:function(e){this.dragging||this.clearValue(e)},handleMouseDown:function(e){if(!(this.props.disabled||"mousedown"===e.type&&0!==e.button)&&"INPUT"!==e.target.tagName){if(this.state.isOpen||e.preventDefault(),e.stopPropagation(),!this.props.searchable)return this.focus(),this.setState({isOpen:!this.state.isOpen});if(this.state.isFocused){this.focus();var t=this.input;"function"==typeof t.getInput&&(t=t.getInput()),t.value="",this.setState({isOpen:!0,isPseudoFocused:!1})}else this._openAfterFocus=!0,this.focus()}},handleMouseDownOnArrow:function(e){this.props.disabled||"mousedown"===e.type&&0!==e.button||this.state.isOpen&&(e.stopPropagation(),e.preventDefault(),this.closeMenu())},handleMouseDownOnMenu:function(e){this.props.disabled||"mousedown"===e.type&&0!==e.button||(e.stopPropagation(),e.preventDefault(),this._openAfterFocus=!0,this.focus())},closeMenu:function(){this.props.onCloseResetsInput?this.setState({isOpen:!1,isPseudoFocused:this.state.isFocused&&!this.props.multi,inputValue:""}):this.setState({isOpen:!1,isPseudoFocused:this.state.isFocused&&!this.props.multi,inputValue:this.state.inputValue}),this.hasScrolledToOption=!1},handleInputFocus:function(e){if(!this.props.disabled){var t=this.state.isOpen||this._openAfterFocus||this.props.openOnFocus;this.props.onFocus&&this.props.onFocus(e);var u={isFocused:!0,isOpen:t};this.props.focusedInputValue&&!this.props.multi&&(u.inputValue=this.props.focusedInputValue(this.props.value)),this.setState(u),this._openAfterFocus=!1}},handleInputBlur:function(e){if(this.menu&&(this.menu===document.activeElement||this.menu.contains(document.activeElement)))return void this.focus();this.props.onBlur&&this.props.onBlur(e);var t={isFocused:!1,isOpen:!1,isPseudoFocused:!1};this.props.onBlurResetsInput&&(t.inputValue=""),this.setState(t)},handleInputChange:function(e){var t=e.target.value;if(this.state.inputValue!==e.target.value&&this.props.onInputChange){var u=this.props.onInputChange(t);null!=u&&"object"!=typeof u&&(t=""+u)}this.setState({isOpen:!0,isPseudoFocused:!1,inputValue:t})},handleKeyDown:function(e){if(!(this.props.disabled||"function"==typeof this.props.onInputKeyDown&&(this.props.onInputKeyDown(e),e.defaultPrevented))){switch(e.keyCode){case 8:return void(!this.state.inputValue&&this.props.backspaceRemoves&&(e.preventDefault(),this.popValue()));case 9:if(e.shiftKey||!this.state.isOpen||!this.props.tabSelectsValue)return;return void this.selectFocusedOption();case 13:if(!this.state.isOpen)return;e.stopPropagation(),this.selectFocusedOption();break;case 27:this.state.isOpen?(this.closeMenu(),e.stopPropagation()):this.props.clearable&&this.props.escapeClearsValue&&(this.clearValue(e),e.stopPropagation());break;case 38:this.focusPreviousOption();break;case 40:this.focusNextOption();break;case 33:this.focusPageUpOption();break;case 34:this.focusPageDownOption();break;case 35:if(e.shiftKey)return;this.focusEndOption();break;case 36:if(e.shiftKey)return;this.focusStartOption();break;default:return}e.preventDefault()}},handleValueClick:function(e,t){this.props.onValueClick&&this.props.onValueClick(e,t)},handleMenuScroll:function(e){if(this.props.onMenuScrollToBottom){var t=e.target;t.scrollHeight>t.offsetHeight&&!(t.scrollHeight-t.offsetHeight-t.scrollTop)&&this.props.onMenuScrollToBottom()}},handleRequired:function(e,t){return!e||(t?0===e.length:0===Object.keys(e).length)},getOptionLabel:function(e){return e[this.props.labelKey]},getValueArray:function(e,t){var u=this,n="object"==typeof t?t:this.props;if(n.multi){if("string"==typeof e&&(e=e.split(n.delimiter)),!Array.isArray(e)){if(null===e||void 0===e)return[];e=[e]}return e.map(function(e){return u.expandValue(e,n)}).filter(function(e){return e})}var s=this.expandValue(e,n);return s?[s]:[]},expandValue:function(e,t){var u=typeof e;if("string"!==u&&"number"!==u&&"boolean"!==u)return e;var n=t.options,s=t.valueKey;if(n)for(var o=0;o<n.length;o++)if(n[o][s]===e)return n[o]},setValue:function(e){var t=this;if(this.props.autoBlur&&this.blurInput(),this.props.onChange){if(this.props.required){var u=this.handleRequired(e,this.props.multi);this.setState({required:u})}this.props.simpleValue&&e&&(e=this.props.multi?e.map(function(e){return e[t.props.valueKey]}).join(this.props.delimiter):e[this.props.valueKey]),this.props.onChange(e)}},selectValue:function(e){var t=this;this.hasScrolledToOption=!1,this.props.multi?this.setState({inputValue:"",focusedIndex:null},function(){t.addValue(e)}):this.setState({isOpen:!1,inputValue:"",isPseudoFocused:this.state.isFocused},function(){t.setValue(e)})},addValue:function(e){var t=this.getValueArray(this.props.value);this.setValue(t.concat(e))},popValue:function(){var e=this.getValueArray(this.props.value);e.length&&e[e.length-1].clearableValue!==!1&&this.setValue(e.slice(0,e.length-1))},removeValue:function(e){var t=this.getValueArray(this.props.value);this.setValue(t.filter(function(t){return t!==e})),this.focus()},clearValue:function(e){e&&"mousedown"===e.type&&0!==e.button||(e.stopPropagation(),e.preventDefault(),this.setValue(this.getResetValue()),this.setState({isOpen:!1,inputValue:""},this.focus))},getResetValue:function(){return void 0!==this.props.resetValue?this.props.resetValue:this.props.multi?[]:null},focusOption:function(e){this.setState({focusedOption:e})},focusNextOption:function(){this.focusAdjacentOption("next")},focusPreviousOption:function(){this.focusAdjacentOption("previous")},focusPageUpOption:function(){this.focusAdjacentOption("page_up")},focusPageDownOption:function(){this.focusAdjacentOption("page_down")},focusStartOption:function(){this.focusAdjacentOption("start")},focusEndOption:function(){this.focusAdjacentOption("end")},focusAdjacentOption:function(e){var t=this._visibleOptions.map(function(e,t){return{option:e,index:t}}).filter(function(e){return!e.option.disabled});if(this._scrollToFocusedOptionOnUpdate=!0,!this.state.isOpen)return void this.setState({isOpen:!0,inputValue:"",focusedOption:this._focusedOption||t["next"===e?0:t.length-1].option});if(t.length){for(var u=-1,n=0;n<t.length;n++)if(this._focusedOption===t[n].option){u=n;break}if("next"===e&&u!==-1)u=(u+1)%t.length;else if("previous"===e)u>0?u-=1:u=t.length-1;else if("start"===e)u=0;else if("end"===e)u=t.length-1;else if("page_up"===e){var s=u-this.props.pageSize;u=s<0?0:s}else if("page_down"===e){var s=u+this.props.pageSize;u=s>t.length-1?t.length-1:s}u===-1&&(u=0),this.setState({focusedIndex:t[u].index,focusedOption:t[u].option})}},getFocusedOption:function(){return this._focusedOption},getInputValue:function(){return this.state.inputValue},selectFocusedOption:function(){var e=this.filterOptions(null),t=this._focusedOption&&e.length>0;return this.props.allowCreate&&!t?this.selectValue(this.state.inputValue):this._focusedOption?this.selectValue(this._focusedOption):void 0},renderLoading:function(){if(this.props.isLoading)return p["default"].createElement("span",{className:"Select-loading-zone","aria-hidden":"true"},p["default"].createElement("span",{className:"Select-loading"}))},renderValue:function(e,t){var u=this,n=this.props.valueRenderer||this.getOptionLabel,s=this.props.valueComponent;if(!e.length)return this.state.inputValue?null:p["default"].createElement("div",{className:"Select-placeholder"},this.props.placeholder);var o=this.props.onValueClick?this.handleValueClick:null;return this.props.multi?e.map(function(e,t){return p["default"].createElement(s,{id:u._instancePrefix+"-value-"+t,instancePrefix:u._instancePrefix,disabled:u.props.disabled||e.clearableValue===!1,key:"value-"+t+"-"+e[u.props.valueKey],onClick:o,onRemove:u.removeValue,value:e},n(e,t),p["default"].createElement("span",{className:"Select-aria-only"}," "))}):this.state.inputValue?void 0:(t&&(o=null),p["default"].createElement(s,{id:this._instancePrefix+"-value-item",disabled:this.props.disabled,instancePrefix:this._instancePrefix,onClick:o,value:e[0]},n(e[0])))},renderInput:function(e,t){var u=this;if(this.props.inputRenderer)return this.props.inputRenderer();var n,s=(0,y["default"])("Select-input",this.props.inputProps.className),a=!!this.state.isOpen,l=(0,y["default"])((n={},i(n,this._instancePrefix+"-list",a),i(n,this._instancePrefix+"-backspace-remove-message",this.props.multi&&!this.props.disabled&&this.state.isFocused&&!this.state.inputValue),n)),d=r({},this.props.inputProps,{role:"combobox","aria-expanded":""+a,"aria-owns":l,"aria-haspopup":""+a,"aria-activedescendant":a?this._instancePrefix+"-option-"+t:this._instancePrefix+"-value","aria-labelledby":this.props["aria-labelledby"],"aria-label":this.props["aria-label"],className:s,tabIndex:this.props.tabIndex,onBlur:this.handleInputBlur,onChange:this.handleInputChange,onFocus:this.handleInputFocus,ref:function(e){return u.input=e},required:this.state.required,value:this.state.inputValue});if(this.props.disabled||!this.props.searchable){var c=this.props.inputProps,f=(c.inputClassName,o(c,["inputClassName"]));return p["default"].createElement("div",r({},f,{role:"combobox","aria-expanded":a,"aria-owns":a?this._instancePrefix+"-list":this._instancePrefix+"-value","aria-activedescendant":a?this._instancePrefix+"-option-"+t:this._instancePrefix+"-value",className:s,tabIndex:this.props.tabIndex||0,onBlur:this.handleInputBlur,onFocus:this.handleInputFocus,ref:function(e){return u.input=e},"aria-readonly":""+!!this.props.disabled,style:{border:0,width:1,display:"inline-block"}}))}return this.props.autosize?p["default"].createElement(h["default"],r({},d,{minWidth:"5"})):p["default"].createElement("div",{className:s},p["default"].createElement("input",d))},renderClear:function(){if(this.props.clearable&&this.props.value&&0!==this.props.value&&(!this.props.multi||this.props.value.length)&&!this.props.disabled&&!this.props.isLoading)return p["default"].createElement("span",{className:"Select-clear-zone",title:this.props.multi?this.props.clearAllText:this.props.clearValueText,"aria-label":this.props.multi?this.props.clearAllText:this.props.clearValueText,onMouseDown:this.clearValue,onTouchStart:this.handleTouchStart,onTouchMove:this.handleTouchMove,onTouchEnd:this.handleTouchEndClearValue},p["default"].createElement("span",{className:"Select-clear",dangerouslySetInnerHTML:{__html:"×"}}))},renderArrow:function(){var e=this.handleMouseDownOnArrow,t=this.props.arrowRenderer({onMouseDown:e});return p["default"].createElement("span",{className:"Select-arrow-zone",onMouseDown:e},t)},filterOptions:function _(e){var t=this.state.inputValue,u=this.props.options||[];if(this.props.filterOptions){var _="function"==typeof this.props.filterOptions?this.props.filterOptions:m["default"];return _(u,t,e,{filterOption:this.props.filterOption,ignoreAccents:this.props.ignoreAccents,ignoreCase:this.props.ignoreCase,labelKey:this.props.labelKey,matchPos:this.props.matchPos,matchProp:this.props.matchProp,valueKey:this.props.valueKey})}return u},onOptionRef:function(e,t){t&&(this.focused=e)},renderMenu:function(e,t,u){return e&&e.length?this.props.menuRenderer({focusedOption:u,focusOption:this.focusOption,instancePrefix:this._instancePrefix,labelKey:this.props.labelKey,onFocus:this.focusOption,onSelect:this.selectValue,optionClassName:this.props.optionClassName,optionComponent:this.props.optionComponent,optionRenderer:this.props.optionRenderer||this.getOptionLabel,options:e,selectValue:this.selectValue,valueArray:t,valueKey:this.props.valueKey,onOptionRef:this.onOptionRef}):this.props.noResultsText?p["default"].createElement("div",{className:"Select-noresults"},this.props.noResultsText):null},renderHiddenField:function(e){ | ||
var t=this;if(this.props.name){if(this.props.joinValues){var u=e.map(function(e){return a(e[t.props.valueKey])}).join(this.props.delimiter);return p["default"].createElement("input",{type:"hidden",ref:function(e){return t.value=e},name:this.props.name,value:u,disabled:this.props.disabled})}return e.map(function(e,u){return p["default"].createElement("input",{key:"hidden."+u,type:"hidden",ref:"value"+u,name:t.props.name,value:a(e[t.props.valueKey]),disabled:t.props.disabled})})}},getFocusableOptionIndex:function(e){var t=this._visibleOptions;if(!t.length)return null;var u=this.state.focusedOption||e;if(u&&!u.disabled){var n=t.indexOf(u);if(n!==-1)return n}for(var s=0;s<t.length;s++)if(!t[s].disabled)return s;return null},renderOuter:function(e,t,u){var n=this,s=this.renderMenu(e,t,u);return s?p["default"].createElement("div",{ref:function(e){return n.menuContainer=e},className:"Select-menu-outer",style:this.props.menuContainerStyle},p["default"].createElement("div",{ref:function(e){return n.menu=e},role:"listbox",className:"Select-menu",id:this._instancePrefix+"-list",style:this.props.menuStyle,onScroll:this.handleMenuScroll,onMouseDown:this.handleMouseDownOnMenu},s)):null},render:function(){var e=this,t=this.getValueArray(this.props.value),u=this._visibleOptions=this.filterOptions(this.props.multi?this.getValueArray(this.props.value):null),n=this.state.isOpen;this.props.multi&&!u.length&&t.length&&!this.state.inputValue&&(n=!1);var s=this.getFocusableOptionIndex(t[0]),o=null;o=null!==s?this._focusedOption=u[s]:this._focusedOption=null;var i=(0,y["default"])("Select",this.props.className,{"Select--multi":this.props.multi,"Select--single":!this.props.multi,"is-disabled":this.props.disabled,"is-focused":this.state.isFocused,"is-loading":this.props.isLoading,"is-open":n,"is-pseudo-focused":this.state.isPseudoFocused,"is-searchable":this.props.searchable,"has-value":t.length}),a=null;return this.props.multi&&!this.props.disabled&&t.length&&!this.state.inputValue&&this.state.isFocused&&this.props.backspaceRemoves&&(a=p["default"].createElement("span",{id:this._instancePrefix+"-backspace-remove-message",className:"Select-aria-only","aria-live":"assertive"},this.props.backspaceToRemoveMessage.replace("{label}",t[t.length-1][this.props.labelKey]))),p["default"].createElement("div",{ref:function(t){return e.wrapper=t},className:i,style:this.props.wrapperStyle},this.renderHiddenField(t),p["default"].createElement("div",{ref:function(t){return e.control=t},className:"Select-control",style:this.props.style,onKeyDown:this.handleKeyDown,onMouseDown:this.handleMouseDown,onTouchEnd:this.handleTouchEnd,onTouchStart:this.handleTouchStart,onTouchMove:this.handleTouchMove},p["default"].createElement("span",{className:"Select-multi-value-wrapper",id:this._instancePrefix+"-value"},this.renderValue(t,n),this.renderInput(t,s)),a,this.renderLoading(),this.renderClear(),this.renderArrow()),n?this.renderOuter(u,this.props.multi?null:t,o):null)}});u["default"]=N,t.exports=u["default"]}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{"./Async":1,"./AsyncCreatable":2,"./Creatable":3,"./Option":4,"./Value":6,"./utils/defaultArrowRenderer":7,"./utils/defaultFilterOptions":8,"./utils/defaultMenuRenderer":9}],6:[function(e,t,u){(function(e){"use strict";function u(e){return e&&e.__esModule?e:{"default":e}}var n="undefined"!=typeof window?window.React:"undefined"!=typeof e?e.React:null,s=u(n),o="undefined"!=typeof window?window.classNames:"undefined"!=typeof e?e.classNames:null,i=u(o),a=s["default"].createClass({displayName:"Value",propTypes:{children:s["default"].PropTypes.node,disabled:s["default"].PropTypes.bool,id:s["default"].PropTypes.string,onClick:s["default"].PropTypes.func,onRemove:s["default"].PropTypes.func,value:s["default"].PropTypes.object.isRequired},handleMouseDown:function(e){if("mousedown"!==e.type||0===e.button)return this.props.onClick?(e.stopPropagation(),void this.props.onClick(this.props.value,e)):void(this.props.value.href&&e.stopPropagation())},onRemove:function(e){e.preventDefault(),e.stopPropagation(),this.props.onRemove(this.props.value)},handleTouchEndRemove:function(e){this.dragging||this.onRemove(e)},handleTouchMove:function(e){this.dragging=!0},handleTouchStart:function(e){this.dragging=!1},renderRemoveIcon:function(){if(!this.props.disabled&&this.props.onRemove)return s["default"].createElement("span",{className:"Select-value-icon","aria-hidden":"true",onMouseDown:this.onRemove,onTouchEnd:this.handleTouchEndRemove,onTouchStart:this.handleTouchStart,onTouchMove:this.handleTouchMove},"×")},renderLabel:function(){var e="Select-value-label";return this.props.onClick||this.props.value.href?s["default"].createElement("a",{className:e,href:this.props.value.href,target:this.props.value.target,onMouseDown:this.handleMouseDown,onTouchEnd:this.handleMouseDown},this.props.children):s["default"].createElement("span",{className:e,role:"option","aria-selected":"true",id:this.props.id},this.props.children)},render:function(){return s["default"].createElement("div",{className:(0,i["default"])("Select-value",this.props.value.className),style:this.props.value.style,title:this.props.value.title},s["default"].createElement("div",{className:"Select-value-inner"},this.renderRemoveIcon(),this.renderLabel()))}});t.exports=a}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{}],7:[function(e,t,u){(function(e){"use strict";function n(e){return e&&e.__esModule?e:{"default":e}}function s(e){var t=e.onMouseDown;return i["default"].createElement("span",{className:"Select-arrow",onMouseDown:t})}Object.defineProperty(u,"__esModule",{value:!0}),u["default"]=s;var o="undefined"!=typeof window?window.React:"undefined"!=typeof e?e.React:null,i=n(o);t.exports=u["default"]}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{}],8:[function(e,t,u){"use strict";function n(e){return e&&e.__esModule?e:{"default":e}}function s(e,t,u,n){var s=this;return n.ignoreAccents&&(t=(0,i["default"])(t)),n.ignoreCase&&(t=t.toLowerCase()),u&&(u=u.map(function(e){return e[n.valueKey]})),e.filter(function(e){if(u&&u.indexOf(e[n.valueKey])>-1)return!1;if(n.filterOption)return n.filterOption.call(s,e,t);if(!t)return!0;var o=String(e[n.valueKey]),a=String(e[n.labelKey]);return n.ignoreAccents&&("label"!==n.matchProp&&(o=(0,i["default"])(o)),"value"!==n.matchProp&&(a=(0,i["default"])(a))),n.ignoreCase&&("label"!==n.matchProp&&(o=o.toLowerCase()),"value"!==n.matchProp&&(a=a.toLowerCase())),"start"===n.matchPos?"label"!==n.matchProp&&o.substr(0,t.length)===t||"value"!==n.matchProp&&a.substr(0,t.length)===t:"label"!==n.matchProp&&o.indexOf(t)>=0||"value"!==n.matchProp&&a.indexOf(t)>=0})}var o=e("./stripDiacritics"),i=n(o);t.exports=s},{"./stripDiacritics":10}],9:[function(e,t,u){(function(e){"use strict";function u(e){return e&&e.__esModule?e:{"default":e}}function n(e){var t=e.focusedOption,u=e.instancePrefix,n=(e.labelKey,e.onFocus),s=e.onSelect,i=e.optionClassName,r=e.optionComponent,l=e.optionRenderer,p=e.options,d=e.valueArray,c=e.valueKey,f=e.onOptionRef,h=r;return p.map(function(e,r){var p=d&&d.indexOf(e)>-1,E=e===t,y=(0,o["default"])(i,{"Select-option":!0,"is-selected":p,"is-focused":E,"is-disabled":e.disabled});return a["default"].createElement(h,{className:y,instancePrefix:u,isDisabled:e.disabled,isFocused:E,isSelected:p,key:"option-"+r+"-"+e[c],onFocus:n,onSelect:s,option:e,optionIndex:r,ref:function(e){f(e,E)}},l(e,r))})}var s="undefined"!=typeof window?window.classNames:"undefined"!=typeof e?e.classNames:null,o=u(s),i="undefined"!=typeof window?window.React:"undefined"!=typeof e?e.React:null,a=u(i);t.exports=n}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{}],10:[function(e,t,u){"use strict";var n=[{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}];t.exports=function(e){for(var t=0;t<n.length;t++)e=e.replace(n[t].letters,n[t].base);return e}},{}]},{},[5])(5)}); |
@@ -7,3 +7,5 @@ /* eslint react/prop-types: 0 */ | ||
import Creatable from './components/Creatable'; | ||
import Contributors from './components/Contributors'; | ||
import GithubUsers from './components/GithubUsers'; | ||
import CustomComponents from './components/CustomComponents'; | ||
@@ -13,2 +15,4 @@ import CustomRender from './components/CustomRender'; | ||
import NumericSelect from './components/NumericSelect'; | ||
import BooleanSelect from './components/BooleanSelect'; | ||
import Virtualized from './components/Virtualized'; | ||
import States from './components/States'; | ||
@@ -20,11 +24,15 @@ | ||
<Multiselect label="Multiselect" /> | ||
<Virtualized label="Virtualized" /> | ||
<Contributors label="Contributors (Async)" /> | ||
<GithubUsers label="Github users (Async with fetch.js)" /> | ||
<NumericSelect label="Numeric Values" /> | ||
<BooleanSelect label="Boolean Values" /> | ||
<CustomRender label="Custom Render Methods"/> | ||
<CustomComponents label="Custom Placeholder, Option and Value Components" /> | ||
{/* | ||
<SelectedValuesField label="Option Creation (tags mode)" options={FLAVOURS} allowCreate hint="Enter a value that's NOT in the list, then hit return" /> | ||
*/} | ||
<CustomComponents label="Custom Placeholder, Option, Value, and Arrow Components" /> | ||
<Creatable | ||
hint="Enter a value that's NOT in the list, then hit return" | ||
label="Custom tag creation" | ||
/> | ||
</div>, | ||
document.getElementById('example') | ||
); |
@@ -17,3 +17,2 @@ import React from 'react'; | ||
onSelect: React.PropTypes.func, | ||
onUnfocus: React.PropTypes.func, | ||
option: React.PropTypes.object.isRequired, | ||
@@ -33,5 +32,2 @@ }, | ||
}, | ||
handleMouseLeave (event) { | ||
this.props.onUnfocus(this.props.option, event); | ||
}, | ||
render () { | ||
@@ -51,3 +47,2 @@ let gravatarStyle = { | ||
onMouseMove={this.handleMouseMove} | ||
onMouseLeave={this.handleMouseLeave} | ||
title={this.props.option.title}> | ||
@@ -105,2 +100,3 @@ <Gravatar email={this.props.option.email} size={GRAVATAR_SIZE} style={gravatarStyle} /> | ||
<Select | ||
arrowRenderer={arrowRenderer} | ||
onChange={this.setValue} | ||
@@ -122,2 +118,8 @@ optionComponent={GravatarOption} | ||
function arrowRenderer () { | ||
return ( | ||
<span>+</span> | ||
); | ||
} | ||
module.exports = UsersField; |
import React from 'react'; | ||
import Select from 'react-select-allow-create'; | ||
import Select from 'react-select'; | ||
import Highlighter from 'react-highlight-words'; | ||
@@ -20,3 +21,8 @@ var DisabledUpsellOptions = React.createClass({ | ||
renderOption: function(option) { | ||
return <span style={{ color: option.color }}>{option.label} {option.link}</span>; | ||
return ( | ||
<Highlighter | ||
searchWords={[this._inputValue]} | ||
textToHighlight={option.label} | ||
/> | ||
); | ||
}, | ||
@@ -36,3 +42,3 @@ renderValue: function(option) { | ||
<Select | ||
placeholder="Select your support level" | ||
onInputChange={(inputValue) => this._inputValue = inputValue} | ||
options={options} | ||
@@ -39,0 +45,0 @@ optionRenderer={this.renderOption} |
@@ -76,7 +76,7 @@ import React from 'react'; | ||
<input type="checkbox" className="checkbox-control" checked={this.state.matchValue} onChange={this.onChangeMatchValue} /> | ||
<span className="checkbox-label">Match value only</span> | ||
<span className="checkbox-label">Match value</span> | ||
</label> | ||
<label className="checkbox"> | ||
<input type="checkbox" className="checkbox-control" checked={this.state.matchLabel} onChange={this.onChangeMatchLabel} /> | ||
<span className="checkbox-label">Match label only</span> | ||
<span className="checkbox-label">Match label</span> | ||
</label> | ||
@@ -83,0 +83,0 @@ <label className="checkbox"> |
# React-Select | ||
## v1.0.0-rc / 2016-09-04 | ||
* fixed; reset value to `[]` when `multi=true`, thanks [Michael Williamson](https://github.com/mwilliamson) | ||
* added; pass index to `renderLabel` method, thanks [nhducit](https://github.com/nhducit) | ||
* fixed; uncontrolled to controlled component warning in React 15.3 | ||
* fixed; props cleanup, thanks [Forbes Lindesay](https://github.com/ForbesLindesay) | ||
* fixed; issue where a value of the number `0` would be assumed to be no value, thanks [Hanwen Cheng](https://github.com/hanwencheng) | ||
* fixed; internal refs converted to callbacks instead of strings, thanks [Johnny Nguyen](https://github.com/gojohnnygo) | ||
* added; optional `instanceId` prop for server-side rendering, thanks [Jevin Anderson](https://github.com/JevinAnderson) | ||
* added; `onCloseResetsInput` prop, thanks [Frankie](https://github.com/frankievx) | ||
* added; `Creatable` component, replaces pre-1.0 `allowCreate` prop, thanks [Brian Vaughn](https://github.com/bvaughn) | ||
## v1.0.0-beta14 / 2016-07-17 | ||
* fixed; `react-input-autosize` has been udpated to `1.1.0`, which includes fixes for the new warnings that React 15.2 logs | ||
* fixed; "Unknown prop `inputClassName` on <div> tag" warning, thanks [Max Stoiber](https://github.com/mxstbr) | ||
* fixed; Removed unnecessary `onUnfocus`, thanks [Johnny Nguyen](https://github.com/gojohnnygo) | ||
* added; Support for react components in `searchPromptText`, thanks [Matt](https://github.com/hellaeon) | ||
* fixed; focus bug on iOS, thanks [Tony deCatanzaro](https://github.com/tonydecat) | ||
* fixed; Async bugs with Promises, thanks [Vladimir](https://github.com/VladimirPal) and [Ian Firkin](https://github.com/lobsteropteryx) | ||
* fixed; `searchingText` bug, thanks [Tony deCatanzaro](https://github.com/tonydecat) | ||
* improved; More antive-like input behaviour, thanks [Johnny Nguyen](https://github.com/gojohnnygo) | ||
* fixed; Added missing unit (px) to `minWidth` attribute, thanks [Ian Witherow](https://github.com/ianwitherow) | ||
* added; Support for assistive technologies, thanks [Dave Brotherstone](https://github.com/bruderstein) | ||
* fixed; React error if `onChange` callback causes a root component to unmount, thanks [Nathan Norton](https://github.com/Xesued) | ||
* fixed; Open menu is now closed if `disabled` becomes true, thanks [Jason Moon](https://github.com/jsnmoon) | ||
* fixed; Prevent `getFocusableOptionIndex` from returning a disabled option, thanks [Brian Powers](https://github.com/brianspowers) | ||
* added; Home, End, Page Up/Down support, thanks [Jason Kadrmas](https://github.com/blackjk3) | ||
* fixed; Don't render `backspaceToRemoveMessage` if `backspaceRemoves` is set to false, thanks [Ryan Zec](https://github.com/ryanzec) | ||
* fixed; Issue with an outline appearing on the auto sized input, thanks [Ryan Zec](https://github.com/ryanzec) | ||
* fixed; Events don't propagate when `esc` is pressed, thanks [Yoshihide Jimbo](https://github.com/jmblog) | ||
* fixed; Update `required` prop based on nextProps on update, thanks [Matt Shwery](https://github.com/mshwery) | ||
* fixed; On focus check whether input ref is a real input or an input component, thanks [Peter Brant](https://github.com/pbrant) and [Greg Poole](https://github.com/gpoole) | ||
Also a big thanks to [Brian Vaughn](https://github.com/bvaughn) for his help triaging issues for this release! | ||
## v1.0.0-beta13 / 2016-05-30 | ||
* added; `inputRenderer` prop, allows you to override the input component, thanks [Sean Burke](https://github.com/leftmostcat) | ||
* added; `openOnFocus` prop, causes the menu to always open when the select control is focused, thanks [HuysentruytRuben](https://github.com/HuysentruytRuben) | ||
* added; `react-virtualised-select` HOC example, thanks [Brian Vaughn](https://github.com/bvaughn) | ||
* added; `tabSelectsValue` prop can be set to false to prevent selection of focused option when tab is pressed, thanks [Byron Anderson](https://github.com/byronanderson) | ||
* added; ability to override `resetValue` when clearing the control, thanks [Alexander Luberg](https://github.com/LubergAlexander) | ||
* added; input can be updated with `onInputChange`, thanks [Brett DeWoody](https://github.com/brettdewoody) | ||
* added; Styles for .is-selected class, thanks [Danny Herran](https://github.com/dherran) | ||
* fixed; `noResultsText` prop type is now `stringOrNode` for Async component, thanks [Michael Groeneman](https://github.com/mgroeneman) | ||
* fixed; `onInputChange` is wrapped by Async component, thanks [Eric O'Connell](https://github.com/drd) | ||
* fixed; `scrollMenuIntoView` behaviour in IE10, thanks [Ivan Jager](https://github.com/aij) | ||
* fixed; isEqualNode replaced with strict equality check, thanks [Alexandre Balhier](https://github.com/abalhier) | ||
* fixed; issue with value object not being passed to `handleRequired`, thanks [Andrew Hite](https://github.com/andyhite) | ||
* fixed; the menu-outer container is no longer rendered when it does not contain anything, thanks [Kuan](https://github.com/khankuan) | ||
* improved; better support for IE8 in styles, thanks [Rockallite Wulf](https://github.com/rockallite) | ||
## v1.0.0-beta12 / 2016-04-02 | ||
* added; `menuRenderer` method and example for effeciently rendering thousands of options, thanks [Brian Vaughn](https://github.com/bvaughn) | ||
* added; `optionClassName` prop, thanks [Max Tyler](https://github.com/iam4x) | ||
## v1.0.0-beta11 / 2016-03-09 | ||
* updated dependencies to allow use with React 15.x | ||
* changed; multiple selected values are now submitted using multiple inputs, thanks [Trinh Hoang Nhu](https://github.com/james4388) | ||
* added; `joinValues` prop to revert the above change and submit multiple values in a single field with the delimiter | ||
@@ -7,0 +67,0 @@ ## v1.0.0-beta10 / 2016-02-23 |
355
lib/Async.js
'use strict'; | ||
Object.defineProperty(exports, '__esModule', { | ||
value: true | ||
}); | ||
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; }; | ||
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; }; })(); | ||
var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } | ||
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } | ||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } | ||
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; } | ||
var _react = require('react'); | ||
@@ -19,151 +33,224 @@ | ||
var requestId = 0; | ||
var propTypes = { | ||
autoload: _react2['default'].PropTypes.bool.isRequired, // automatically call the `loadOptions` prop on-mount; defaults to true | ||
cache: _react2['default'].PropTypes.any, // object to use to cache results; set to null/false to disable caching | ||
children: _react2['default'].PropTypes.func.isRequired, // Child function responsible for creating the inner Select component; (props: Object): PropTypes.element | ||
ignoreAccents: _react2['default'].PropTypes.bool, // strip diacritics when filtering; defaults to true | ||
ignoreCase: _react2['default'].PropTypes.bool, // perform case-insensitive filtering; defaults to true | ||
loadingPlaceholder: _react2['default'].PropTypes.oneOfType([// replaces the placeholder while options are loading | ||
_react2['default'].PropTypes.string, _react2['default'].PropTypes.node]), | ||
loadOptions: _react2['default'].PropTypes.func.isRequired, // callback to load options asynchronously; (inputValue: string, callback: Function): ?Promise | ||
options: _react.PropTypes.array.isRequired, // array of options | ||
placeholder: _react2['default'].PropTypes.oneOfType([// field placeholder, displayed when there's no value (shared with Select) | ||
_react2['default'].PropTypes.string, _react2['default'].PropTypes.node]), | ||
noResultsText: _react2['default'].PropTypes.oneOfType([// field noResultsText, displayed when no options come back from the server | ||
_react2['default'].PropTypes.string, _react2['default'].PropTypes.node]), | ||
searchPromptText: _react2['default'].PropTypes.oneOfType([// label to prompt for search input | ||
_react2['default'].PropTypes.string, _react2['default'].PropTypes.node]), | ||
onInputChange: _react2['default'].PropTypes.func, // optional for keeping track of what is being typed | ||
value: _react2['default'].PropTypes.any }; | ||
function initCache(cache) { | ||
if (cache && typeof cache !== 'object') { | ||
cache = {}; | ||
// initial field value | ||
var defaultProps = { | ||
autoload: true, | ||
cache: {}, | ||
children: defaultChildren, | ||
ignoreAccents: true, | ||
ignoreCase: true, | ||
loadingPlaceholder: 'Loading...', | ||
options: [], | ||
searchPromptText: 'Type to search' | ||
}; | ||
var Async = (function (_Component) { | ||
_inherits(Async, _Component); | ||
function Async(props, context) { | ||
_classCallCheck(this, Async); | ||
_get(Object.getPrototypeOf(Async.prototype), 'constructor', this).call(this, props, context); | ||
this.state = { | ||
isLoading: false, | ||
options: props.options | ||
}; | ||
this._onInputChange = this._onInputChange.bind(this); | ||
} | ||
return cache ? cache : null; | ||
} | ||
function updateCache(cache, input, data) { | ||
if (!cache) return; | ||
cache[input] = data; | ||
} | ||
_createClass(Async, [{ | ||
key: 'componentDidMount', | ||
value: function componentDidMount() { | ||
var autoload = this.props.autoload; | ||
function getFromCache(cache, input) { | ||
if (!cache) return; | ||
for (var i = input.length; i >= 0; --i) { | ||
var cacheKey = input.slice(0, i); | ||
if (cache[cacheKey] && (input === cacheKey || cache[cacheKey].complete)) { | ||
return cache[cacheKey]; | ||
if (autoload) { | ||
this.loadOptions(''); | ||
} | ||
} | ||
} | ||
} | ||
}, { | ||
key: 'componentWillUpdate', | ||
value: function componentWillUpdate(nextProps, nextState) { | ||
var _this = this; | ||
function thenPromise(promise, callback) { | ||
if (!promise || typeof promise.then !== 'function') return; | ||
return promise.then(function (data) { | ||
callback(null, data); | ||
}, function (err) { | ||
callback(err); | ||
}); | ||
} | ||
var propertiesToSync = ['options']; | ||
propertiesToSync.forEach(function (prop) { | ||
if (_this.props[prop] !== nextProps[prop]) { | ||
_this.setState(_defineProperty({}, prop, nextProps[prop])); | ||
} | ||
}); | ||
} | ||
}, { | ||
key: 'clearOptions', | ||
value: function clearOptions() { | ||
this.setState({ options: [] }); | ||
} | ||
}, { | ||
key: 'loadOptions', | ||
value: function loadOptions(inputValue) { | ||
var _this2 = this; | ||
var stringOrNode = _react2['default'].PropTypes.oneOfType([_react2['default'].PropTypes.string, _react2['default'].PropTypes.node]); | ||
var _props = this.props; | ||
var cache = _props.cache; | ||
var loadOptions = _props.loadOptions; | ||
var Async = _react2['default'].createClass({ | ||
displayName: 'Async', | ||
if (cache && cache.hasOwnProperty(inputValue)) { | ||
this.setState({ | ||
options: cache[inputValue] | ||
}); | ||
propTypes: { | ||
cache: _react2['default'].PropTypes.any, // object to use to cache results, can be null to disable cache | ||
ignoreAccents: _react2['default'].PropTypes.bool, // whether to strip diacritics when filtering (shared with Select) | ||
ignoreCase: _react2['default'].PropTypes.bool, // whether to perform case-insensitive filtering (shared with Select) | ||
isLoading: _react2['default'].PropTypes.bool, // overrides the isLoading state when set to true | ||
loadOptions: _react2['default'].PropTypes.func.isRequired, // function to call to load options asynchronously | ||
loadingPlaceholder: _react2['default'].PropTypes.string, // replaces the placeholder while options are loading | ||
minimumInput: _react2['default'].PropTypes.number, // the minimum number of characters that trigger loadOptions | ||
noResultsText: _react2['default'].PropTypes.string, // placeholder displayed when there are no matching search results (shared with Select) | ||
placeholder: stringOrNode, // field placeholder, displayed when there's no value (shared with Select) | ||
searchPromptText: _react2['default'].PropTypes.string, // label to prompt for search input | ||
searchingText: _react2['default'].PropTypes.string }, | ||
// message to display while options are loading | ||
getDefaultProps: function getDefaultProps() { | ||
return { | ||
cache: true, | ||
ignoreAccents: true, | ||
ignoreCase: true, | ||
loadingPlaceholder: 'Loading...', | ||
minimumInput: 0, | ||
searchingText: 'Searching...', | ||
searchPromptText: 'Type to search' | ||
}; | ||
}, | ||
getInitialState: function getInitialState() { | ||
return { | ||
cache: initCache(this.props.cache), | ||
isLoading: false, | ||
options: [] | ||
}; | ||
}, | ||
componentWillMount: function componentWillMount() { | ||
this._lastInput = ''; | ||
}, | ||
componentDidMount: function componentDidMount() { | ||
this.loadOptions(''); | ||
}, | ||
componentWillReceiveProps: function componentWillReceiveProps(nextProps) { | ||
if (nextProps.cache !== this.props.cache) { | ||
this.setState({ | ||
cache: initCache(nextProps.cache) | ||
}); | ||
return; | ||
} | ||
var callback = function callback(error, data) { | ||
if (callback === _this2._callback) { | ||
_this2._callback = null; | ||
var options = data && data.options || []; | ||
if (cache) { | ||
cache[inputValue] = options; | ||
} | ||
_this2.setState({ | ||
isLoading: false, | ||
options: options | ||
}); | ||
} | ||
}; | ||
// Ignore all but the most recent request | ||
this._callback = callback; | ||
var promise = loadOptions(inputValue, callback); | ||
if (promise) { | ||
promise.then(function (data) { | ||
return callback(null, data); | ||
}, function (error) { | ||
return callback(error); | ||
}); | ||
} | ||
if (this._callback && !this.state.isLoading) { | ||
this.setState({ | ||
isLoading: true | ||
}); | ||
} | ||
return inputValue; | ||
} | ||
}, | ||
focus: function focus() { | ||
this.refs.select.focus(); | ||
}, | ||
resetState: function resetState() { | ||
this._currentRequestId = -1; | ||
this.setState({ | ||
isLoading: false, | ||
options: [] | ||
}); | ||
}, | ||
getResponseHandler: function getResponseHandler(input) { | ||
var _this = this; | ||
}, { | ||
key: '_onInputChange', | ||
value: function _onInputChange(inputValue) { | ||
var _props2 = this.props; | ||
var ignoreAccents = _props2.ignoreAccents; | ||
var ignoreCase = _props2.ignoreCase; | ||
var onInputChange = _props2.onInputChange; | ||
var _requestId = this._currentRequestId = requestId++; | ||
return function (err, data) { | ||
if (err) throw err; | ||
if (!_this.isMounted()) return; | ||
updateCache(_this.state.cache, input, data); | ||
if (_requestId !== _this._currentRequestId) return; | ||
_this.setState({ | ||
isLoading: false, | ||
options: data && data.options || [] | ||
}); | ||
}; | ||
}, | ||
loadOptions: function loadOptions(input) { | ||
if (this.props.ignoreAccents) input = (0, _utilsStripDiacritics2['default'])(input); | ||
if (this.props.ignoreCase) input = input.toLowerCase(); | ||
this._lastInput = input; | ||
if (input.length < this.props.minimumInput) { | ||
return this.resetState(); | ||
if (ignoreAccents) { | ||
inputValue = (0, _utilsStripDiacritics2['default'])(inputValue); | ||
} | ||
if (ignoreCase) { | ||
inputValue = inputValue.toLowerCase(); | ||
} | ||
if (onInputChange) { | ||
onInputChange(inputValue); | ||
} | ||
return this.loadOptions(inputValue); | ||
} | ||
var cacheResult = getFromCache(this.state.cache, input); | ||
if (cacheResult) { | ||
return this.setState({ | ||
options: cacheResult.options | ||
}); | ||
}, { | ||
key: 'inputValue', | ||
value: function inputValue() { | ||
if (this.select) { | ||
return this.select.state.inputValue; | ||
} | ||
return ''; | ||
} | ||
this.setState({ | ||
isLoading: true | ||
}); | ||
var responseHandler = this.getResponseHandler(input); | ||
return thenPromise(this.props.loadOptions(input, responseHandler), responseHandler); | ||
}, | ||
render: function render() { | ||
var noResultsText = this.props.noResultsText; | ||
var _state = this.state; | ||
var isLoading = _state.isLoading; | ||
var options = _state.options; | ||
}, { | ||
key: 'noResultsText', | ||
value: function noResultsText() { | ||
var _props3 = this.props; | ||
var loadingPlaceholder = _props3.loadingPlaceholder; | ||
var noResultsText = _props3.noResultsText; | ||
var searchPromptText = _props3.searchPromptText; | ||
var isLoading = this.state.isLoading; | ||
if (this.props.isLoading) isLoading = true; | ||
var placeholder = isLoading ? this.props.loadingPlaceholder : this.props.placeholder; | ||
if (!options.length) { | ||
if (this._lastInput.length < this.props.minimumInput) noResultsText = this.props.searchPromptText; | ||
if (isLoading) noResultsText = this.props.searchingText; | ||
var inputValue = this.inputValue(); | ||
if (isLoading) { | ||
return loadingPlaceholder; | ||
} | ||
if (inputValue && noResultsText) { | ||
return noResultsText; | ||
} | ||
return searchPromptText; | ||
} | ||
return _react2['default'].createElement(_Select2['default'], _extends({}, this.props, { | ||
ref: 'select', | ||
isLoading: isLoading, | ||
noResultsText: noResultsText, | ||
onInputChange: this.loadOptions, | ||
options: options, | ||
placeholder: placeholder | ||
})); | ||
} | ||
}); | ||
}, { | ||
key: 'render', | ||
value: function render() { | ||
var _this3 = this; | ||
module.exports = Async; | ||
var _props4 = this.props; | ||
var children = _props4.children; | ||
var loadingPlaceholder = _props4.loadingPlaceholder; | ||
var placeholder = _props4.placeholder; | ||
var _state = this.state; | ||
var isLoading = _state.isLoading; | ||
var options = _state.options; | ||
var props = { | ||
noResultsText: this.noResultsText(), | ||
placeholder: isLoading ? loadingPlaceholder : placeholder, | ||
options: isLoading && loadingPlaceholder ? [] : options, | ||
ref: function ref(_ref) { | ||
return _this3.select = _ref; | ||
}, | ||
onChange: function onChange(newValues) { | ||
if (_this3.props.value && newValues.length > _this3.props.value.length) { | ||
_this3.clearOptions(); | ||
} | ||
_this3.props.onChange(newValues); | ||
} | ||
}; | ||
return children(_extends({}, this.props, props, { | ||
isLoading: isLoading, | ||
onInputChange: this._onInputChange | ||
})); | ||
} | ||
}]); | ||
return Async; | ||
})(_react.Component); | ||
exports['default'] = Async; | ||
Async.propTypes = propTypes; | ||
Async.defaultProps = defaultProps; | ||
function defaultChildren(props) { | ||
return _react2['default'].createElement(_Select2['default'], props); | ||
}; | ||
module.exports = exports['default']; |
@@ -19,2 +19,3 @@ 'use strict'; | ||
className: _react2['default'].PropTypes.string, // className (based on mouse position) | ||
instancePrefix: _react2['default'].PropTypes.string.isRequired, // unique prefix for the ids (used for aria) | ||
isDisabled: _react2['default'].PropTypes.bool, // the option is disabled | ||
@@ -26,4 +27,5 @@ isFocused: _react2['default'].PropTypes.bool, // the option is focused | ||
onUnfocus: _react2['default'].PropTypes.func, // method to handle mouseLeave on option element | ||
option: _react2['default'].PropTypes.object.isRequired }, | ||
// object that is base for that option | ||
option: _react2['default'].PropTypes.object.isRequired, // object that is base for that option | ||
optionIndex: _react2['default'].PropTypes.number }, | ||
// index of the option, used to generate unique ids for aria | ||
blockEvent: function blockEvent(event) { | ||
@@ -80,3 +82,6 @@ event.preventDefault(); | ||
render: function render() { | ||
var option = this.props.option; | ||
var _props = this.props; | ||
var option = _props.option; | ||
var instancePrefix = _props.instancePrefix; | ||
var optionIndex = _props.optionIndex; | ||
@@ -95,2 +100,3 @@ var className = (0, _classnames2['default'])(this.props.className, option.className); | ||
style: option.style, | ||
role: 'option', | ||
onMouseDown: this.handleMouseDown, | ||
@@ -102,2 +108,3 @@ onMouseEnter: this.handleMouseEnter, | ||
onTouchEnd: this.handleTouchEnd, | ||
id: instancePrefix + '-option-' + optionIndex, | ||
title: option.title }, | ||
@@ -104,0 +111,0 @@ this.props.children |
@@ -0,1 +1,7 @@ | ||
/*! | ||
Copyright (c) 2016 Jed Watson. | ||
Licensed under the MIT License (MIT), see | ||
http://jedwatson.github.io/react-select | ||
*/ | ||
'use strict'; | ||
@@ -11,2 +17,6 @@ | ||
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; } | ||
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } | ||
var _react = require('react'); | ||
@@ -28,6 +38,14 @@ | ||
var _utilsStripDiacritics = require('./utils/stripDiacritics'); | ||
var _utilsDefaultArrowRenderer = require('./utils/defaultArrowRenderer'); | ||
var _utilsStripDiacritics2 = _interopRequireDefault(_utilsStripDiacritics); | ||
var _utilsDefaultArrowRenderer2 = _interopRequireDefault(_utilsDefaultArrowRenderer); | ||
var _utilsDefaultFilterOptions = require('./utils/defaultFilterOptions'); | ||
var _utilsDefaultFilterOptions2 = _interopRequireDefault(_utilsDefaultFilterOptions); | ||
var _utilsDefaultMenuRenderer = require('./utils/defaultMenuRenderer'); | ||
var _utilsDefaultMenuRenderer2 = _interopRequireDefault(_utilsDefaultMenuRenderer); | ||
var _Async = require('./Async'); | ||
@@ -37,2 +55,10 @@ | ||
var _AsyncCreatable = require('./AsyncCreatable'); | ||
var _AsyncCreatable2 = _interopRequireDefault(_AsyncCreatable); | ||
var _Creatable = require('./Creatable'); | ||
var _Creatable2 = _interopRequireDefault(_Creatable); | ||
var _Option = require('./Option'); | ||
@@ -47,6 +73,11 @@ | ||
function stringifyValue(value) { | ||
if (typeof value === 'object') { | ||
var valueType = typeof value; | ||
if (valueType === 'string') { | ||
return value; | ||
} else if (valueType === 'object') { | ||
return JSON.stringify(value); | ||
} else if (valueType === 'number' || valueType === 'boolean') { | ||
return String(value); | ||
} else { | ||
return value; | ||
return ''; | ||
} | ||
@@ -57,2 +88,4 @@ } | ||
var instanceId = 1; | ||
var Select = _react2['default'].createClass({ | ||
@@ -64,7 +97,10 @@ | ||
addLabelText: _react2['default'].PropTypes.string, // placeholder displayed when you want to add a label on a multi-value input | ||
'aria-label': _react2['default'].PropTypes.string, // Aria label (for assistive tech) | ||
'aria-labelledby': _react2['default'].PropTypes.string, // HTML ID of an element that should be used as the label (for assistive tech) | ||
arrowRenderer: _react2['default'].PropTypes.func, // Create drop-down caret element | ||
autoBlur: _react2['default'].PropTypes.bool, // automatically blur the component when an option is selected | ||
autofocus: _react2['default'].PropTypes.bool, // autofocus the component on mount | ||
autosize: _react2['default'].PropTypes.bool, // whether to enable autosizing or not | ||
allowCreate: _react2['default'].PropTypes.bool, // whether to allow creation of new entries | ||
autoBlur: _react2['default'].PropTypes.bool, | ||
autofocus: _react2['default'].PropTypes.bool, // autofocus the component on mount | ||
backspaceRemoves: _react2['default'].PropTypes.bool, // whether backspace removes an item if there is no text input | ||
backspaceToRemoveMessage: _react2['default'].PropTypes.string, // Message to use for screenreaders to press backspace to remove the current item - {label} is replaced with the item label | ||
className: _react2['default'].PropTypes.string, // className for the outer element | ||
@@ -83,2 +119,4 @@ clearAllText: stringOrNode, // title for the "clear" control when multi: true | ||
inputProps: _react2['default'].PropTypes.object, // custom attributes for the Input | ||
inputRenderer: _react2['default'].PropTypes.func, // returns a custom input component | ||
instanceId: _react2['default'].PropTypes.string, // set the components instanceId | ||
isLoading: _react2['default'].PropTypes.bool, // whether the Select is loading externally or not (such as options being loaded) | ||
@@ -91,6 +129,6 @@ joinValues: _react2['default'].PropTypes.bool, // joins multiple values into a single form field with the delimiter (legacy mode) | ||
menuContainerStyle: _react2['default'].PropTypes.object, // optional style to apply to the menu container | ||
menuRenderer: _react2['default'].PropTypes.func, // renders a custom menu with options | ||
menuStyle: _react2['default'].PropTypes.object, // optional style to apply to the menu | ||
multi: _react2['default'].PropTypes.bool, // multi-value input | ||
name: _react2['default'].PropTypes.string, // generates a hidden <input /> tag with this field name for html forms | ||
newOptionCreator: _react2['default'].PropTypes.func, // factory to create new options when allowCreate set | ||
noResultsText: stringOrNode, // placeholder displayed when there are no matching search results | ||
@@ -101,12 +139,19 @@ onBlur: _react2['default'].PropTypes.func, // onBlur handler: function (event) {} | ||
onClose: _react2['default'].PropTypes.func, // fires when the menu is closed | ||
onCloseResetsInput: _react2['default'].PropTypes.bool, // whether input is cleared when menu is closed through the arrow | ||
onFocus: _react2['default'].PropTypes.func, // onFocus handler: function (event) {} | ||
onInputChange: _react2['default'].PropTypes.func, // onInputChange handler: function (inputValue) {} | ||
onInputKeyDown: _react2['default'].PropTypes.func, // input keyDown handler: function (event) {} | ||
onMenuScrollToBottom: _react2['default'].PropTypes.func, // fires when the menu is scrolled to the bottom; can be used to paginate options | ||
onOpen: _react2['default'].PropTypes.func, // fires when the menu is opened | ||
onValueClick: _react2['default'].PropTypes.func, // onClick handler for value labels: function (value, event) {} | ||
openAfterFocus: _react2['default'].PropTypes.bool, // boolean to enable opening dropdown when focused | ||
openOnFocus: _react2['default'].PropTypes.bool, // always open options menu on focus | ||
optionClassName: _react2['default'].PropTypes.string, // additional class(es) to apply to the <Option /> elements | ||
optionComponent: _react2['default'].PropTypes.func, // option component to render in dropdown | ||
optionRenderer: _react2['default'].PropTypes.func, // optionRenderer: function (option) {} | ||
options: _react2['default'].PropTypes.array, // array of options | ||
pageSize: _react2['default'].PropTypes.number, // number of entries to page when using page up/down keys | ||
placeholder: stringOrNode, // field placeholder, displayed when there's no value | ||
required: _react2['default'].PropTypes.bool, // applies HTML5 required attribute when needed | ||
resetValue: _react2['default'].PropTypes.any, // value to use when you clear the control | ||
scrollMenuIntoView: _react2['default'].PropTypes.bool, // boolean to enable the viewport to shift so that the full menu fully visible when engaged | ||
@@ -117,2 +162,3 @@ searchable: _react2['default'].PropTypes.bool, // whether to enable searching feature or not | ||
tabIndex: _react2['default'].PropTypes.string, // optional tab index of the control | ||
tabSelectsValue: _react2['default'].PropTypes.bool, // whether to treat tabbing out while focused to be value selection | ||
value: _react2['default'].PropTypes.any, // initial field value | ||
@@ -122,6 +168,7 @@ valueComponent: _react2['default'].PropTypes.func, // value component to render | ||
valueRenderer: _react2['default'].PropTypes.func, // valueRenderer: function (option) {} | ||
wrapperStyle: _react2['default'].PropTypes.object }, | ||
wrapperStyle: _react2['default'].PropTypes.object, // optional style to apply to the component wrapper | ||
inputValue: _react2['default'].PropTypes.string | ||
}, | ||
// optional style to apply to the component wrapper | ||
statics: { Async: _Async2['default'] }, | ||
statics: { Async: _Async2['default'], AsyncCreatable: _AsyncCreatable2['default'], Creatable: _Creatable2['default'] }, | ||
@@ -131,5 +178,6 @@ getDefaultProps: function getDefaultProps() { | ||
addLabelText: 'Add "{label}"?', | ||
arrowRenderer: _utilsDefaultArrowRenderer2['default'], | ||
autosize: true, | ||
allowCreate: false, | ||
backspaceRemoves: true, | ||
backspaceToRemoveMessage: 'Press backspace to remove {label}', | ||
clearable: true, | ||
@@ -141,3 +189,3 @@ clearAllText: 'Clear all', | ||
escapeClearsValue: true, | ||
filterOptions: true, | ||
filterOptions: _utilsDefaultFilterOptions2['default'], | ||
ignoreAccents: true, | ||
@@ -152,6 +200,10 @@ ignoreCase: true, | ||
menuBuffer: 0, | ||
menuRenderer: _utilsDefaultMenuRenderer2['default'], | ||
multi: false, | ||
noResultsText: 'No results found', | ||
onBlurResetsInput: true, | ||
onCloseResetsInput: true, | ||
openAfterFocus: false, | ||
optionComponent: _Option2['default'], | ||
pageSize: 5, | ||
placeholder: 'Select...', | ||
@@ -162,4 +214,6 @@ required: false, | ||
simpleValue: false, | ||
tabSelectsValue: true, | ||
valueComponent: _Value2['default'], | ||
valueKey: 'value' | ||
valueKey: 'value', | ||
inputValue: '' | ||
}; | ||
@@ -170,11 +224,21 @@ }, | ||
return { | ||
inputValue: '', | ||
inputValue: this.props.inputValue, | ||
isFocused: false, | ||
isLoading: false, | ||
isOpen: false, | ||
isPseudoFocused: false, | ||
required: this.props.required && this.handleRequired(this.props.value, this.props.multi) | ||
required: false | ||
}; | ||
}, | ||
componentWillMount: function componentWillMount() { | ||
this._instancePrefix = 'react-select-' + (this.props.instanceId || ++instanceId) + '-'; | ||
var valueArray = this.getValueArray(this.props.value); | ||
if (this.props.required) { | ||
this.setState({ | ||
required: this.handleRequired(valueArray[0], this.props.multi) | ||
}); | ||
} | ||
}, | ||
componentDidMount: function componentDidMount() { | ||
@@ -187,7 +251,12 @@ if (this.props.autofocus) { | ||
componentWillReceiveProps: function componentWillReceiveProps(nextProps) { | ||
if (this.props.value !== nextProps.value && nextProps.required) { | ||
var valueArray = this.getValueArray(nextProps.value, nextProps); | ||
if (nextProps.required) { | ||
this.setState({ | ||
required: this.handleRequired(nextProps.value, nextProps.multi) | ||
required: this.handleRequired(valueArray[0], nextProps.multi) | ||
}); | ||
} | ||
if (nextProps.inputValue !== '') { | ||
this.setState({ inputValue: nextProps.inputValue }); | ||
} | ||
}, | ||
@@ -197,2 +266,3 @@ | ||
if (nextState.isOpen !== this.state.isOpen) { | ||
this.toggleTouchOutsideEvent(nextState.isOpen); | ||
var handler = nextState.isOpen ? nextProps.onOpen : nextProps.onClose; | ||
@@ -205,5 +275,5 @@ handler && handler(); | ||
// focus to the selected option | ||
if (this.refs.menu && this.refs.focused && this.state.isOpen && !this.hasScrolledToOption) { | ||
var focusedOptionNode = _reactDom2['default'].findDOMNode(this.refs.focused); | ||
var menuNode = _reactDom2['default'].findDOMNode(this.refs.menu); | ||
if (this.menu && this.focused && this.state.isOpen && !this.hasScrolledToOption) { | ||
var focusedOptionNode = _reactDom2['default'].findDOMNode(this.focused); | ||
var menuNode = _reactDom2['default'].findDOMNode(this.menu); | ||
menuNode.scrollTop = focusedOptionNode.offsetTop; | ||
@@ -215,9 +285,6 @@ this.hasScrolledToOption = true; | ||
if (prevState.inputValue !== this.state.inputValue && this.props.onInputChange) { | ||
this.props.onInputChange(this.state.inputValue); | ||
} | ||
if (this._scrollToFocusedOptionOnUpdate && this.refs.focused && this.refs.menu) { | ||
if (this._scrollToFocusedOptionOnUpdate && this.focused && this.menu) { | ||
this._scrollToFocusedOptionOnUpdate = false; | ||
var focusedDOM = _reactDom2['default'].findDOMNode(this.refs.focused); | ||
var menuDOM = _reactDom2['default'].findDOMNode(this.refs.menu); | ||
var focusedDOM = _reactDom2['default'].findDOMNode(this.focused); | ||
var menuDOM = _reactDom2['default'].findDOMNode(this.menu); | ||
var focusedRect = focusedDOM.getBoundingClientRect(); | ||
@@ -229,6 +296,6 @@ var menuRect = menuDOM.getBoundingClientRect(); | ||
} | ||
if (this.props.scrollMenuIntoView && this.refs.menuContainer) { | ||
var menuContainerRect = this.refs.menuContainer.getBoundingClientRect(); | ||
if (this.props.scrollMenuIntoView && this.menuContainer) { | ||
var menuContainerRect = this.menuContainer.getBoundingClientRect(); | ||
if (window.innerHeight < menuContainerRect.bottom + this.props.menuBuffer) { | ||
window.scrollTo(0, window.scrollY + menuContainerRect.bottom + this.props.menuBuffer - window.innerHeight); | ||
window.scrollBy(0, menuContainerRect.bottom + this.props.menuBuffer - window.innerHeight); | ||
} | ||
@@ -238,13 +305,51 @@ } | ||
this.setState({ isFocused: false }); // eslint-disable-line react/no-did-update-set-state | ||
this.closeMenu(); | ||
} | ||
}, | ||
componentWillUnmount: function componentWillUnmount() { | ||
if (!document.removeEventListener && document.detachEvent) { | ||
document.detachEvent('ontouchstart', this.handleTouchOutside); | ||
} else { | ||
document.removeEventListener('touchstart', this.handleTouchOutside); | ||
} | ||
}, | ||
toggleTouchOutsideEvent: function toggleTouchOutsideEvent(enabled) { | ||
if (enabled) { | ||
if (!document.addEventListener && document.attachEvent) { | ||
document.attachEvent('ontouchstart', this.handleTouchOutside); | ||
} else { | ||
document.addEventListener('touchstart', this.handleTouchOutside); | ||
} | ||
} else { | ||
if (!document.removeEventListener && document.detachEvent) { | ||
document.detachEvent('ontouchstart', this.handleTouchOutside); | ||
} else { | ||
document.removeEventListener('touchstart', this.handleTouchOutside); | ||
} | ||
} | ||
}, | ||
handleTouchOutside: function handleTouchOutside(event) { | ||
// handle touch outside on ios to dismiss menu | ||
if (this.wrapper && !this.wrapper.contains(event.target)) { | ||
this.closeMenu(); | ||
} | ||
}, | ||
focus: function focus() { | ||
if (!this.refs.input) return; | ||
this.refs.input.focus(); | ||
if (!this.input) return; | ||
this.input.focus(); | ||
if (this.props.openAfterFocus) { | ||
this.setState({ | ||
isOpen: true | ||
}); | ||
} | ||
}, | ||
blurInput: function blurInput() { | ||
if (!this.refs.input) return; | ||
this.refs.input.blur(); | ||
if (!this.input) return; | ||
this.input.blur(); | ||
}, | ||
@@ -287,2 +392,6 @@ | ||
if (event.target.tagName === 'INPUT') { | ||
return; | ||
} | ||
if (!this.state.isOpen) { | ||
@@ -302,2 +411,16 @@ event.preventDefault(); | ||
if (this.state.isFocused) { | ||
// On iOS, we can get into a state where we think the input is focused but it isn't really, | ||
// since iOS ignores programmatic calls to input.focus() that weren't triggered by a click event. | ||
// Call focus() again here to be safe. | ||
this.focus(); | ||
var input = this.input; | ||
if (typeof input.getInput === 'function') { | ||
// Get the actual DOM input if the ref is an <AutosizeInput /> component | ||
input = input.getInput(); | ||
} | ||
// clears the value so that the cursor will be at the end of input when the component re-renders | ||
input.value = ''; | ||
// if the input is focused, ensure the menu is open | ||
@@ -346,7 +469,15 @@ this.setState({ | ||
closeMenu: function closeMenu() { | ||
this.setState({ | ||
isOpen: false, | ||
isPseudoFocused: this.state.isFocused && !this.props.multi, | ||
inputValue: '' | ||
}); | ||
if (this.props.onCloseResetsInput) { | ||
this.setState({ | ||
isOpen: false, | ||
isPseudoFocused: this.state.isFocused && !this.props.multi, | ||
inputValue: '' | ||
}); | ||
} else { | ||
this.setState({ | ||
isOpen: false, | ||
isPseudoFocused: this.state.isFocused && !this.props.multi, | ||
inputValue: this.state.inputValue | ||
}); | ||
} | ||
this.hasScrolledToOption = false; | ||
@@ -356,3 +487,4 @@ }, | ||
handleInputFocus: function handleInputFocus(event) { | ||
var isOpen = this.state.isOpen || this._openAfterFocus; | ||
if (this.props.disabled) return; | ||
var isOpen = this.state.isOpen || this._openAfterFocus || this.props.openOnFocus; | ||
if (this.props.onFocus) { | ||
@@ -373,3 +505,5 @@ this.props.onFocus(event); | ||
handleInputBlur: function handleInputBlur(event) { | ||
if (this.refs.menu && document.activeElement.isEqualNode(this.refs.menu)) { | ||
// The check for menu.contains(activeElement) is necessary to prevent IE11's scrollbar from closing the menu in certain contexts. | ||
if (this.menu && (this.menu === document.activeElement || this.menu.contains(document.activeElement))) { | ||
this.focus(); | ||
return; | ||
@@ -393,6 +527,16 @@ } | ||
handleInputChange: function handleInputChange(event) { | ||
var newInputValue = event.target.value; | ||
if (this.state.inputValue !== event.target.value && this.props.onInputChange) { | ||
var nextState = this.props.onInputChange(newInputValue); | ||
// Note: != used deliberately here to catch undefined and null | ||
if (nextState != null && typeof nextState !== 'object') { | ||
newInputValue = '' + nextState; | ||
} | ||
} | ||
this.setState({ | ||
isOpen: true, | ||
isPseudoFocused: false, | ||
inputValue: event.target.value | ||
inputValue: newInputValue | ||
}); | ||
@@ -403,2 +547,10 @@ }, | ||
if (this.props.disabled) return; | ||
if (typeof this.props.onInputKeyDown === 'function') { | ||
this.props.onInputKeyDown(event); | ||
if (event.defaultPrevented) { | ||
return; | ||
} | ||
} | ||
switch (event.keyCode) { | ||
@@ -414,3 +566,3 @@ case 8: | ||
// tab | ||
if (event.shiftKey || !this.state.isOpen) { | ||
if (event.shiftKey || !this.state.isOpen || !this.props.tabSelectsValue) { | ||
return; | ||
@@ -430,4 +582,6 @@ } | ||
this.closeMenu(); | ||
event.stopPropagation(); | ||
} else if (this.props.clearable && this.props.escapeClearsValue) { | ||
this.clearValue(event); | ||
event.stopPropagation(); | ||
} | ||
@@ -443,11 +597,24 @@ break; | ||
break; | ||
// case 188: // , | ||
// if (this.props.allowCreate && this.props.multi) { | ||
// event.preventDefault(); | ||
// event.stopPropagation(); | ||
// this.selectFocusedOption(); | ||
// } else { | ||
// return; | ||
// } | ||
// break; | ||
case 33: | ||
// page up | ||
this.focusPageUpOption(); | ||
break; | ||
case 34: | ||
// page down | ||
this.focusPageDownOption(); | ||
break; | ||
case 35: | ||
// end key | ||
if (event.shiftKey) { | ||
return; | ||
} | ||
this.focusEndOption(); | ||
break; | ||
case 36: | ||
// home key | ||
if (event.shiftKey) { | ||
return; | ||
} | ||
this.focusStartOption(); | ||
break; | ||
default: | ||
@@ -482,6 +649,15 @@ return; | ||
getValueArray: function getValueArray() { | ||
var value = this.props.value; | ||
if (this.props.multi) { | ||
if (typeof value === 'string') value = value.split(this.props.delimiter); | ||
/** | ||
* Turns a value into an array from the given options | ||
* @param {String|Number|Array} value - the value of the select input | ||
* @param {Object} nextProps - optionally specify the nextProps so the returned array uses the latest configuration | ||
* @returns {Array} the value of the select represented in an array | ||
*/ | ||
getValueArray: function getValueArray(value, nextProps) { | ||
var _this = this; | ||
/** support optionally passing in the `nextProps` so `componentWillReceiveProps` updates will function as expected */ | ||
var props = typeof nextProps === 'object' ? nextProps : this.props; | ||
if (props.multi) { | ||
if (typeof value === 'string') value = value.split(props.delimiter); | ||
if (!Array.isArray(value)) { | ||
@@ -491,15 +667,22 @@ if (value === null || value === undefined) return []; | ||
} | ||
return value.map(this.expandValue).filter(function (i) { | ||
return value.map(function (value) { | ||
return _this.expandValue(value, props); | ||
}).filter(function (i) { | ||
return i; | ||
}); | ||
} | ||
var expandedValue = this.expandValue(value); | ||
var expandedValue = this.expandValue(value, props); | ||
return expandedValue ? [expandedValue] : []; | ||
}, | ||
expandValue: function expandValue(value) { | ||
if (typeof value !== 'string' && typeof value !== 'number') return value; | ||
var _props = this.props; | ||
var options = _props.options; | ||
var valueKey = _props.valueKey; | ||
/** | ||
* Retrieve a value from the given options and valueKey | ||
* @param {String|Number|Array} value - the selected value(s) | ||
* @param {Object} props - the Select component's props (or nextProps) | ||
*/ | ||
expandValue: function expandValue(value, props) { | ||
var valueType = typeof value; | ||
if (valueType !== 'string' && valueType !== 'number' && valueType !== 'boolean') return value; | ||
var options = props.options; | ||
var valueKey = props.valueKey; | ||
@@ -513,3 +696,3 @@ if (!options) return; | ||
setValue: function setValue(value) { | ||
var _this = this; | ||
var _this2 = this; | ||
@@ -526,3 +709,3 @@ if (this.props.autoBlur) { | ||
value = this.props.multi ? value.map(function (i) { | ||
return i[_this.props.valueKey]; | ||
return i[_this2.props.valueKey]; | ||
}).join(this.props.delimiter) : value[this.props.valueKey]; | ||
@@ -534,10 +717,14 @@ } | ||
selectValue: function selectValue(value) { | ||
var _this3 = this; | ||
//NOTE: update value in the callback to make sure the input value is empty so that there are no styling issues (Chrome had issue otherwise) | ||
this.hasScrolledToOption = false; | ||
if (this.props.multi) { | ||
this.addValue(value); | ||
this.setState({ | ||
inputValue: '' | ||
inputValue: '', | ||
focusedIndex: null | ||
}, function () { | ||
_this3.addValue(value); | ||
}); | ||
} else { | ||
this.setValue(value); | ||
this.setState({ | ||
@@ -547,2 +734,4 @@ isOpen: false, | ||
isPseudoFocused: this.state.isFocused | ||
}, function () { | ||
_this3.setValue(value); | ||
}); | ||
@@ -553,3 +742,3 @@ } | ||
addValue: function addValue(value) { | ||
var valueArray = this.getValueArray(); | ||
var valueArray = this.getValueArray(this.props.value); | ||
this.setValue(valueArray.concat(value)); | ||
@@ -559,3 +748,3 @@ }, | ||
popValue: function popValue() { | ||
var valueArray = this.getValueArray(); | ||
var valueArray = this.getValueArray(this.props.value); | ||
if (!valueArray.length) return; | ||
@@ -567,3 +756,3 @@ if (valueArray[valueArray.length - 1].clearableValue === false) return; | ||
removeValue: function removeValue(value) { | ||
var valueArray = this.getValueArray(); | ||
var valueArray = this.getValueArray(this.props.value); | ||
this.setValue(valueArray.filter(function (i) { | ||
@@ -583,3 +772,3 @@ return i !== value; | ||
event.preventDefault(); | ||
this.setValue(null); | ||
this.setValue(this.getResetValue()); | ||
this.setState({ | ||
@@ -591,2 +780,12 @@ isOpen: false, | ||
getResetValue: function getResetValue() { | ||
if (this.props.resetValue !== undefined) { | ||
return this.props.resetValue; | ||
} else if (this.props.multi) { | ||
return []; | ||
} else { | ||
return null; | ||
} | ||
}, | ||
focusOption: function focusOption(option) { | ||
@@ -606,5 +805,23 @@ this.setState({ | ||
focusPageUpOption: function focusPageUpOption() { | ||
this.focusAdjacentOption('page_up'); | ||
}, | ||
focusPageDownOption: function focusPageDownOption() { | ||
this.focusAdjacentOption('page_down'); | ||
}, | ||
focusStartOption: function focusStartOption() { | ||
this.focusAdjacentOption('start'); | ||
}, | ||
focusEndOption: function focusEndOption() { | ||
this.focusAdjacentOption('end'); | ||
}, | ||
focusAdjacentOption: function focusAdjacentOption(dir) { | ||
var options = this._visibleOptions.filter(function (i) { | ||
return !i.disabled; | ||
var options = this._visibleOptions.map(function (option, index) { | ||
return { option: option, index: index }; | ||
}).filter(function (option) { | ||
return !option.option.disabled; | ||
}); | ||
@@ -616,3 +833,3 @@ this._scrollToFocusedOptionOnUpdate = true; | ||
inputValue: '', | ||
focusedOption: this._focusedOption || options[dir === 'next' ? 0 : options.length - 1] | ||
focusedOption: this._focusedOption || options[dir === 'next' ? 0 : options.length - 1].option | ||
}); | ||
@@ -624,3 +841,3 @@ return; | ||
for (var i = 0; i < options.length; i++) { | ||
if (this._focusedOption === options[i]) { | ||
if (this._focusedOption === options[i].option) { | ||
focusedIndex = i; | ||
@@ -630,17 +847,48 @@ break; | ||
} | ||
var focusedOption = options[0]; | ||
if (dir === 'next' && focusedIndex > -1 && focusedIndex < options.length - 1) { | ||
focusedOption = options[focusedIndex + 1]; | ||
if (dir === 'next' && focusedIndex !== -1) { | ||
focusedIndex = (focusedIndex + 1) % options.length; | ||
} else if (dir === 'previous') { | ||
if (focusedIndex > 0) { | ||
focusedOption = options[focusedIndex - 1]; | ||
focusedIndex = focusedIndex - 1; | ||
} else { | ||
focusedOption = options[options.length - 1]; | ||
focusedIndex = options.length - 1; | ||
} | ||
} else if (dir === 'start') { | ||
focusedIndex = 0; | ||
} else if (dir === 'end') { | ||
focusedIndex = options.length - 1; | ||
} else if (dir === 'page_up') { | ||
var potentialIndex = focusedIndex - this.props.pageSize; | ||
if (potentialIndex < 0) { | ||
focusedIndex = 0; | ||
} else { | ||
focusedIndex = potentialIndex; | ||
} | ||
} else if (dir === 'page_down') { | ||
var potentialIndex = focusedIndex + this.props.pageSize; | ||
if (potentialIndex > options.length - 1) { | ||
focusedIndex = options.length - 1; | ||
} else { | ||
focusedIndex = potentialIndex; | ||
} | ||
} | ||
if (focusedIndex === -1) { | ||
focusedIndex = 0; | ||
} | ||
this.setState({ | ||
focusedOption: focusedOption | ||
focusedIndex: options[focusedIndex].index, | ||
focusedOption: options[focusedIndex].option | ||
}); | ||
}, | ||
getFocusedOption: function getFocusedOption() { | ||
return this._focusedOption; | ||
}, | ||
getInputValue: function getInputValue() { | ||
return this.state.inputValue; | ||
}, | ||
selectFocusedOption: function selectFocusedOption() { | ||
@@ -667,3 +915,3 @@ var options = this.filterOptions(null); | ||
renderValue: function renderValue(valueArray, isOpen) { | ||
var _this2 = this; | ||
var _this4 = this; | ||
@@ -685,9 +933,16 @@ var renderLabel = this.props.valueRenderer || this.getOptionLabel; | ||
{ | ||
disabled: _this2.props.disabled || value.clearableValue === false, | ||
key: 'value-' + i + '-' + value[_this2.props.valueKey], | ||
id: _this4._instancePrefix + '-value-' + i, | ||
instancePrefix: _this4._instancePrefix, | ||
disabled: _this4.props.disabled || value.clearableValue === false, | ||
key: 'value-' + i + '-' + value[_this4.props.valueKey], | ||
onClick: onClick, | ||
onRemove: _this2.removeValue, | ||
onRemove: _this4.removeValue, | ||
value: value | ||
}, | ||
renderLabel(value) | ||
renderLabel(value, i), | ||
_react2['default'].createElement( | ||
'span', | ||
{ className: 'Select-aria-only' }, | ||
' ' | ||
) | ||
); | ||
@@ -700,3 +955,5 @@ }); | ||
{ | ||
id: this._instancePrefix + '-value-item', | ||
disabled: this.props.disabled, | ||
instancePrefix: this._instancePrefix, | ||
onClick: onClick, | ||
@@ -710,16 +967,25 @@ value: valueArray[0] | ||
renderInput: function renderInput(valueArray) { | ||
var className = (0, _classnames2['default'])('Select-input', this.props.inputProps.className); | ||
if (this.props.disabled || !this.props.searchable) { | ||
return _react2['default'].createElement('div', _extends({}, this.props.inputProps, { | ||
renderInput: function renderInput(valueArray, focusedOptionIndex) { | ||
var _this5 = this; | ||
if (this.props.inputRenderer) { | ||
return this.props.inputRenderer(); | ||
} else { | ||
var _classNames; | ||
var className = (0, _classnames2['default'])('Select-input', this.props.inputProps.className); | ||
var isOpen = !!this.state.isOpen; | ||
var ariaOwns = (0, _classnames2['default'])((_classNames = {}, _defineProperty(_classNames, this._instancePrefix + '-list', isOpen), _defineProperty(_classNames, this._instancePrefix + '-backspace-remove-message', this.props.multi && !this.props.disabled && this.state.isFocused && !this.state.inputValue), _classNames)); | ||
// TODO: Check how this project includes Object.assign() | ||
var inputProps = _extends({}, this.props.inputProps, { | ||
role: 'combobox', | ||
'aria-expanded': '' + isOpen, | ||
'aria-owns': ariaOwns, | ||
'aria-haspopup': '' + isOpen, | ||
'aria-activedescendant': isOpen ? this._instancePrefix + '-option-' + focusedOptionIndex : this._instancePrefix + '-value', | ||
'aria-labelledby': this.props['aria-labelledby'], | ||
'aria-label': this.props['aria-label'], | ||
className: className, | ||
tabIndex: this.props.tabIndex || 0, | ||
onBlur: this.handleInputBlur, | ||
onFocus: this.handleInputFocus, | ||
ref: 'input', | ||
style: { border: 0, width: 1, display: 'inline-block' } })); | ||
} | ||
if (this.props.autosize) { | ||
return _react2['default'].createElement(_reactInputAutosize2['default'], _extends({}, this.props.inputProps, { | ||
className: className, | ||
tabIndex: this.props.tabIndex, | ||
@@ -729,25 +995,44 @@ onBlur: this.handleInputBlur, | ||
onFocus: this.handleInputFocus, | ||
minWidth: '5', | ||
ref: 'input', | ||
ref: function ref(_ref) { | ||
return _this5.input = _ref; | ||
}, | ||
required: this.state.required, | ||
value: this.state.inputValue | ||
})); | ||
}); | ||
if (this.props.disabled || !this.props.searchable) { | ||
var _props$inputProps = this.props.inputProps; | ||
var inputClassName = _props$inputProps.inputClassName; | ||
var divProps = _objectWithoutProperties(_props$inputProps, ['inputClassName']); | ||
return _react2['default'].createElement('div', _extends({}, divProps, { | ||
role: 'combobox', | ||
'aria-expanded': isOpen, | ||
'aria-owns': isOpen ? this._instancePrefix + '-list' : this._instancePrefix + '-value', | ||
'aria-activedescendant': isOpen ? this._instancePrefix + '-option-' + focusedOptionIndex : this._instancePrefix + '-value', | ||
className: className, | ||
tabIndex: this.props.tabIndex || 0, | ||
onBlur: this.handleInputBlur, | ||
onFocus: this.handleInputFocus, | ||
ref: function (ref) { | ||
return _this5.input = ref; | ||
}, | ||
'aria-readonly': '' + !!this.props.disabled, | ||
style: { border: 0, width: 1, display: 'inline-block' } })); | ||
} | ||
if (this.props.autosize) { | ||
return _react2['default'].createElement(_reactInputAutosize2['default'], _extends({}, inputProps, { minWidth: '5' })); | ||
} | ||
return _react2['default'].createElement( | ||
'div', | ||
{ className: className }, | ||
_react2['default'].createElement('input', inputProps) | ||
); | ||
} | ||
return _react2['default'].createElement( | ||
'div', | ||
{ className: className }, | ||
_react2['default'].createElement('input', _extends({}, this.props.inputProps, { | ||
tabIndex: this.props.tabIndex, | ||
onBlur: this.handleInputBlur, | ||
onChange: this.handleInputChange, | ||
onFocus: this.handleInputFocus, | ||
ref: 'input', | ||
required: this.state.required, | ||
value: this.state.inputValue | ||
})) | ||
); | ||
}, | ||
renderClear: function renderClear() { | ||
if (!this.props.clearable || !this.props.value || this.props.multi && !this.props.value.length || this.props.disabled || this.props.isLoading) return; | ||
if (!this.props.clearable || !this.props.value || this.props.value === 0 || this.props.multi && !this.props.value.length || this.props.disabled || this.props.isLoading) return; | ||
return _react2['default'].createElement( | ||
@@ -760,3 +1045,4 @@ 'span', | ||
onTouchMove: this.handleTouchMove, | ||
onTouchEnd: this.handleTouchEndClearValue }, | ||
onTouchEnd: this.handleTouchEndClearValue | ||
}, | ||
_react2['default'].createElement('span', { className: 'Select-clear', dangerouslySetInnerHTML: { __html: '×' } }) | ||
@@ -767,6 +1053,12 @@ ); | ||
renderArrow: function renderArrow() { | ||
var onMouseDown = this.handleMouseDownOnArrow; | ||
var arrow = this.props.arrowRenderer({ onMouseDown: onMouseDown }); | ||
return _react2['default'].createElement( | ||
'span', | ||
{ className: 'Select-arrow-zone', onMouseDown: this.handleMouseDownOnArrow }, | ||
_react2['default'].createElement('span', { className: 'Select-arrow', onMouseDown: this.handleMouseDownOnArrow }) | ||
{ | ||
className: 'Select-arrow-zone', | ||
onMouseDown: onMouseDown | ||
}, | ||
arrow | ||
); | ||
@@ -776,34 +1068,17 @@ }, | ||
filterOptions: function filterOptions(excludeOptions) { | ||
var _this3 = this; | ||
var filterValue = this.state.inputValue; | ||
var options = this.props.options || []; | ||
if (typeof this.props.filterOptions === 'function') { | ||
return this.props.filterOptions.call(this, options, filterValue, excludeOptions); | ||
} else if (this.props.filterOptions) { | ||
if (this.props.ignoreAccents) { | ||
filterValue = (0, _utilsStripDiacritics2['default'])(filterValue); | ||
} | ||
if (this.props.ignoreCase) { | ||
filterValue = filterValue.toLowerCase(); | ||
} | ||
if (excludeOptions) excludeOptions = excludeOptions.map(function (i) { | ||
return i[_this3.props.valueKey]; | ||
if (this.props.filterOptions) { | ||
// Maintain backwards compatibility with boolean attribute | ||
var filterOptions = typeof this.props.filterOptions === 'function' ? this.props.filterOptions : _utilsDefaultFilterOptions2['default']; | ||
return filterOptions(options, filterValue, excludeOptions, { | ||
filterOption: this.props.filterOption, | ||
ignoreAccents: this.props.ignoreAccents, | ||
ignoreCase: this.props.ignoreCase, | ||
labelKey: this.props.labelKey, | ||
matchPos: this.props.matchPos, | ||
matchProp: this.props.matchProp, | ||
valueKey: this.props.valueKey | ||
}); | ||
return options.filter(function (option) { | ||
if (excludeOptions && excludeOptions.indexOf(option[_this3.props.valueKey]) > -1) return false; | ||
if (_this3.props.filterOption) return _this3.props.filterOption.call(_this3, option, filterValue); | ||
if (!filterValue) return true; | ||
var valueTest = String(option[_this3.props.valueKey]); | ||
var labelTest = String(option[_this3.props.labelKey]); | ||
if (_this3.props.ignoreAccents) { | ||
if (_this3.props.matchProp !== 'label') valueTest = (0, _utilsStripDiacritics2['default'])(valueTest); | ||
if (_this3.props.matchProp !== 'value') labelTest = (0, _utilsStripDiacritics2['default'])(labelTest); | ||
} | ||
if (_this3.props.ignoreCase) { | ||
if (_this3.props.matchProp !== 'label') valueTest = valueTest.toLowerCase(); | ||
if (_this3.props.matchProp !== 'value') labelTest = labelTest.toLowerCase(); | ||
} | ||
return _this3.props.matchPos === 'start' ? _this3.props.matchProp !== 'label' && valueTest.substr(0, filterValue.length) === filterValue || _this3.props.matchProp !== 'value' && labelTest.substr(0, filterValue.length) === filterValue : _this3.props.matchProp !== 'label' && valueTest.indexOf(filterValue) >= 0 || _this3.props.matchProp !== 'value' && labelTest.indexOf(filterValue) >= 0; | ||
}); | ||
} else { | ||
@@ -814,42 +1089,26 @@ return options; | ||
onOptionRef: function onOptionRef(ref, isFocused) { | ||
if (isFocused) { | ||
this.focused = ref; | ||
} | ||
}, | ||
renderMenu: function renderMenu(options, valueArray, focusedOption) { | ||
var _this4 = this; | ||
if (options && options.length) { | ||
var _ret = (function () { | ||
var Option = _this4.props.optionComponent; | ||
var renderLabel = _this4.props.optionRenderer || _this4.getOptionLabel; | ||
return { | ||
v: options.map(function (option, i) { | ||
var isSelected = valueArray && valueArray.indexOf(option) > -1; | ||
var isFocused = option === focusedOption; | ||
var optionRef = isFocused ? 'focused' : null; | ||
var optionClass = (0, _classnames2['default'])({ | ||
'Select-option': true, | ||
'is-selected': isSelected, | ||
'is-focused': isFocused, | ||
'is-disabled': option.disabled | ||
}); | ||
return _react2['default'].createElement( | ||
Option, | ||
{ | ||
className: optionClass, | ||
isDisabled: option.disabled, | ||
isFocused: isFocused, | ||
key: 'option-' + i + '-' + option[_this4.props.valueKey], | ||
onSelect: _this4.selectValue, | ||
onFocus: _this4.focusOption, | ||
option: option, | ||
isSelected: isSelected, | ||
ref: optionRef | ||
}, | ||
renderLabel(option) | ||
); | ||
}) | ||
}; | ||
})(); | ||
if (typeof _ret === 'object') return _ret.v; | ||
return this.props.menuRenderer({ | ||
focusedOption: focusedOption, | ||
focusOption: this.focusOption, | ||
instancePrefix: this._instancePrefix, | ||
labelKey: this.props.labelKey, | ||
onFocus: this.focusOption, | ||
onSelect: this.selectValue, | ||
optionClassName: this.props.optionClassName, | ||
optionComponent: this.props.optionComponent, | ||
optionRenderer: this.props.optionRenderer || this.getOptionLabel, | ||
options: options, | ||
selectValue: this.selectValue, | ||
valueArray: valueArray, | ||
valueKey: this.props.valueKey, | ||
onOptionRef: this.onOptionRef | ||
}); | ||
} else if (this.props.noResultsText) { | ||
@@ -867,3 +1126,3 @@ return _react2['default'].createElement( | ||
renderHiddenField: function renderHiddenField(valueArray) { | ||
var _this5 = this; | ||
var _this6 = this; | ||
@@ -873,7 +1132,9 @@ if (!this.props.name) return; | ||
var value = valueArray.map(function (i) { | ||
return stringifyValue(i[_this5.props.valueKey]); | ||
return stringifyValue(i[_this6.props.valueKey]); | ||
}).join(this.props.delimiter); | ||
return _react2['default'].createElement('input', { | ||
type: 'hidden', | ||
ref: 'value', | ||
ref: function (ref) { | ||
return _this6.value = ref; | ||
}, | ||
name: this.props.name, | ||
@@ -887,26 +1148,70 @@ value: value, | ||
ref: 'value' + index, | ||
name: _this5.props.name, | ||
value: stringifyValue(item[_this5.props.valueKey]), | ||
disabled: _this5.props.disabled }); | ||
name: _this6.props.name, | ||
value: stringifyValue(item[_this6.props.valueKey]), | ||
disabled: _this6.props.disabled }); | ||
}); | ||
}, | ||
getFocusableOption: function getFocusableOption(selectedOption) { | ||
getFocusableOptionIndex: function getFocusableOptionIndex(selectedOption) { | ||
var options = this._visibleOptions; | ||
if (!options.length) return; | ||
if (!options.length) return null; | ||
var focusedOption = this.state.focusedOption || selectedOption; | ||
if (focusedOption && options.indexOf(focusedOption) > -1) return focusedOption; | ||
if (focusedOption && !focusedOption.disabled) { | ||
var focusedOptionIndex = options.indexOf(focusedOption); | ||
if (focusedOptionIndex !== -1) { | ||
return focusedOptionIndex; | ||
} | ||
} | ||
for (var i = 0; i < options.length; i++) { | ||
if (!options[i].disabled) return options[i]; | ||
if (!options[i].disabled) return i; | ||
} | ||
return null; | ||
}, | ||
renderOuter: function renderOuter(options, valueArray, focusedOption) { | ||
var _this7 = this; | ||
var menu = this.renderMenu(options, valueArray, focusedOption); | ||
if (!menu) { | ||
return null; | ||
} | ||
return _react2['default'].createElement( | ||
'div', | ||
{ ref: function (ref) { | ||
return _this7.menuContainer = ref; | ||
}, className: 'Select-menu-outer', style: this.props.menuContainerStyle }, | ||
_react2['default'].createElement( | ||
'div', | ||
{ ref: function (ref) { | ||
return _this7.menu = ref; | ||
}, role: 'listbox', className: 'Select-menu', id: this._instancePrefix + '-list', | ||
style: this.props.menuStyle, | ||
onScroll: this.handleMenuScroll, | ||
onMouseDown: this.handleMouseDownOnMenu }, | ||
menu | ||
) | ||
); | ||
}, | ||
render: function render() { | ||
var valueArray = this.getValueArray(); | ||
var options = this._visibleOptions = this.filterOptions(this.props.multi ? valueArray : null); | ||
var _this8 = this; | ||
var valueArray = this.getValueArray(this.props.value); | ||
var options = this._visibleOptions = this.filterOptions(this.props.multi ? this.getValueArray(this.props.value) : null); | ||
var isOpen = this.state.isOpen; | ||
if (this.props.multi && !options.length && valueArray.length && !this.state.inputValue) isOpen = false; | ||
var focusedOption = this._focusedOption = this.getFocusableOption(valueArray[0]); | ||
var focusedOptionIndex = this.getFocusableOptionIndex(valueArray[0]); | ||
var focusedOption = null; | ||
if (focusedOptionIndex !== null) { | ||
focusedOption = this._focusedOption = options[focusedOptionIndex]; | ||
} else { | ||
focusedOption = this._focusedOption = null; | ||
} | ||
var className = (0, _classnames2['default'])('Select', this.props.className, { | ||
'Select--multi': this.props.multi, | ||
'Select--single': !this.props.multi, | ||
'is-disabled': this.props.disabled, | ||
@@ -920,9 +1225,25 @@ 'is-focused': this.state.isFocused, | ||
}); | ||
var removeMessage = null; | ||
if (this.props.multi && !this.props.disabled && valueArray.length && !this.state.inputValue && this.state.isFocused && this.props.backspaceRemoves) { | ||
removeMessage = _react2['default'].createElement( | ||
'span', | ||
{ id: this._instancePrefix + '-backspace-remove-message', className: 'Select-aria-only', 'aria-live': 'assertive' }, | ||
this.props.backspaceToRemoveMessage.replace('{label}', valueArray[valueArray.length - 1][this.props.labelKey]) | ||
); | ||
} | ||
return _react2['default'].createElement( | ||
'div', | ||
{ ref: 'wrapper', className: className, style: this.props.wrapperStyle }, | ||
{ ref: function (ref) { | ||
return _this8.wrapper = ref; | ||
}, | ||
className: className, | ||
style: this.props.wrapperStyle }, | ||
this.renderHiddenField(valueArray), | ||
_react2['default'].createElement( | ||
'div', | ||
{ ref: 'control', | ||
{ ref: function (ref) { | ||
return _this8.control = ref; | ||
}, | ||
className: 'Select-control', | ||
@@ -934,5 +1255,11 @@ style: this.props.style, | ||
onTouchStart: this.handleTouchStart, | ||
onTouchMove: this.handleTouchMove }, | ||
this.renderValue(valueArray, isOpen), | ||
this.renderInput(valueArray), | ||
onTouchMove: this.handleTouchMove | ||
}, | ||
_react2['default'].createElement( | ||
'span', | ||
{ className: 'Select-multi-value-wrapper', id: this._instancePrefix + '-value' }, | ||
this.renderValue(valueArray, isOpen), | ||
this.renderInput(valueArray, focusedOptionIndex) | ||
), | ||
removeMessage, | ||
this.renderLoading(), | ||
@@ -942,14 +1269,3 @@ this.renderClear(), | ||
), | ||
isOpen ? _react2['default'].createElement( | ||
'div', | ||
{ ref: 'menuContainer', className: 'Select-menu-outer', style: this.props.menuContainerStyle }, | ||
_react2['default'].createElement( | ||
'div', | ||
{ ref: 'menu', className: 'Select-menu', | ||
style: this.props.menuStyle, | ||
onScroll: this.handleMenuScroll, | ||
onMouseDown: this.handleMouseDownOnMenu }, | ||
this.renderMenu(options, !this.props.multi ? valueArray : null, focusedOption) | ||
) | ||
) : null | ||
isOpen ? this.renderOuter(options, !this.props.multi ? valueArray : null, focusedOption) : null | ||
); | ||
@@ -956,0 +1272,0 @@ } |
@@ -20,2 +20,3 @@ 'use strict'; | ||
disabled: _react2['default'].PropTypes.bool, // disabled prop passed to ReactSelect | ||
id: _react2['default'].PropTypes.string, // Unique id for the value - used for aria | ||
onClick: _react2['default'].PropTypes.func, // method to handle click on value label | ||
@@ -70,2 +71,3 @@ onRemove: _react2['default'].PropTypes.func, // method to handle removal of the value | ||
{ className: 'Select-value-icon', | ||
'aria-hidden': 'true', | ||
onMouseDown: this.onRemove, | ||
@@ -87,3 +89,3 @@ onTouchEnd: this.handleTouchEndRemove, | ||
'span', | ||
{ className: className }, | ||
{ className: className, role: 'option', 'aria-selected': 'true', id: this.props.id }, | ||
this.props.children | ||
@@ -100,4 +102,8 @@ ); | ||
}, | ||
this.renderRemoveIcon(), | ||
this.renderLabel() | ||
_react2['default'].createElement( | ||
'div', | ||
{ className: 'Select-value-inner' }, | ||
this.renderRemoveIcon(), | ||
this.renderLabel() | ||
) | ||
); | ||
@@ -104,0 +110,0 @@ } |
{ | ||
"name": "react-select-allow-create", | ||
"version": "1.0.0-beta11.9", | ||
"version": "1.0.0-beta11.11", | ||
"description": "A Select control built with and for ReactJS", | ||
@@ -14,4 +14,4 @@ "main": "lib/Select.js", | ||
"dependencies": { | ||
"classnames": "^2.2.3", | ||
"react-input-autosize": "^0.6.9" | ||
"classnames": "^2.2.4", | ||
"react-input-autosize": "^1.1.0" | ||
}, | ||
@@ -21,24 +21,29 @@ "devDependencies": { | ||
"babel-eslint": "^4.1.3", | ||
"chai": "^3.4.1", | ||
"coveralls": "^2.11.6", | ||
"chai": "^3.5.0", | ||
"coveralls": "^2.11.12", | ||
"eslint": "^1.10.3", | ||
"eslint-plugin-react": "^3.15.0", | ||
"gulp": "^3.9.0", | ||
"istanbul": "^0.4.2", | ||
"jsdom": "^7.2.2", | ||
"mocha": "^2.3.4", | ||
"react": "^0.14 || ^15.0.0-rc || ^15.0.0", | ||
"react-addons-test-utils": "^0.14 || ^15.0.0-rc || ^15.0.0", | ||
"gulp": "^3.9.1", | ||
"isomorphic-fetch": "^2.2.1", | ||
"istanbul": "^0.4.5", | ||
"jsdom": "^9.5.0", | ||
"mocha": "^3.0.2", | ||
"react": "^15.0", | ||
"react-addons-shallow-compare": "^15.0", | ||
"react-addons-test-utils": "^15.0", | ||
"react-component-gulp-tasks": "^0.7.7", | ||
"react-dom": "^0.14 || ^15.0.0-rc || ^15.0.0", | ||
"react-gravatar": "^2.3.0", | ||
"sinon": "^1.17.2", | ||
"unexpected": "^10.7.0", | ||
"unexpected-dom": "^3.0.2", | ||
"unexpected-react": "^0.5.1", | ||
"unexpected-sinon": "^9.0.4" | ||
"react-dom": "^15.0", | ||
"react-gravatar": "^2.4.5", | ||
"react-highlight-words": "^0.3.0", | ||
"react-virtualized": "^7.23.0", | ||
"react-virtualized-select": "^1.4.0", | ||
"sinon": "^1.17.5", | ||
"unexpected": "^10.17.0", | ||
"unexpected-dom": "^3.1.0", | ||
"unexpected-react": "^3.2.4", | ||
"unexpected-sinon": "^10.4.0" | ||
}, | ||
"peerDependencies": { | ||
"react": "^0.14 || ^15.0.0-rc || ^15.0.0", | ||
"react-dom": "^0.14 || ^15.0.0-rc || ^15.0.0" | ||
"react": "^0.14 || ^15.0.0-rc || ^15.0", | ||
"react-dom": "^0.14 || ^15.0.0-rc || ^15.0" | ||
}, | ||
@@ -63,3 +68,3 @@ "browserify-shim": { | ||
"start": "gulp dev", | ||
"test": "mocha --compilers js:babel/register", | ||
"test": "npm run lint && mocha --compilers js:babel/register", | ||
"watch": "gulp watch:lib" | ||
@@ -66,0 +71,0 @@ }, |
182
README.md
@@ -1,4 +0,5 @@ | ||
[![NPM](https://badge.fury.io/js/react-select.png)](https://www.npmjs.com/package/react-select) | ||
[![NPM](https://img.shields.io/npm/v/react-select.svg)](https://www.npmjs.com/package/react-select) | ||
[![Build Status](https://travis-ci.org/JedWatson/react-select.svg?branch=master)](https://travis-ci.org/JedWatson/react-select) | ||
[![Coverage Status](https://coveralls.io/repos/JedWatson/react-select/badge.svg?branch=master&service=github)](https://coveralls.io/github/JedWatson/react-select?branch=master) | ||
[![Supported by Thinkmill](https://thinkmill.github.io/badge/heart.svg)](http://thinkmill.com.au/?utm_source=github&utm_medium=badge&utm_campaign=react-select) | ||
@@ -11,10 +12,8 @@ React-Select | ||
## New version 1.0.0-beta | ||
## New version 1.0.0-rc | ||
I've nearly completed a major rewrite of this component (see issue [#568](https://github.com/JedWatson/react-select/issues/568) for details and progress). The new code has been merged into `master`, and `react-select@1.0.0-beta` has been published to npm and bower. | ||
I've nearly completed a major rewrite of this component (see issue [#568](https://github.com/JedWatson/react-select/issues/568) for details and progress). The new code has been merged into `master`, and `react-select@1.0.0-rc` has been published to npm and bower. | ||
1.0.0 has some breaking changes. The documentation below still needs to be updated for the new API; notes on the changes can be found in [CHANGES.md](https://github.com/JedWatson/react-select/blob/master/CHANGES.md) and will be finalised into [HISTORY.md](https://github.com/JedWatson/react-select/blob/master/HISTORY.md) soon. | ||
1.0.0 has some breaking changes. The documentation is still being updated for the new API; notes on the changes can be found in [CHANGES.md](https://github.com/JedWatson/react-select/blob/master/CHANGES.md) and will be finalised into [HISTORY.md](https://github.com/JedWatson/react-select/blob/master/HISTORY.md) soon. | ||
Our tests need some major updates to work with the new API (see [#571](https://github.com/JedWatson/react-select/issues/571)) and are causing the build to fail, but the component is stable and robust in actual usage. | ||
Testing, feedback and PRs for the new version are appreciated. | ||
@@ -47,9 +46,23 @@ | ||
You can also use the standalone build by including `dist/react-select.js` and `dist/react-select.css` in your page. If you use this, make sure you have already included the following dependencies: | ||
At this point you can import react-select and its styles in your application as follows: | ||
* [React](http://facebook.github.io/react/) | ||
* [classNames](http://jedwatson.github.io/classnames/) | ||
* [react-input-autosize](https://github.com/JedWatson/react-input-autosize) | ||
```js | ||
import Select from 'react-select'; | ||
// Be sure to include styles at some point, probably during your bootstrapping | ||
import 'react-select/dist/react-select.css'; | ||
``` | ||
You can also use the standalone build by including `react-select.js` and `react-select.css` in your page. (If you do this though you'll also need to include the dependencies.) For example: | ||
```html | ||
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script> | ||
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script> | ||
<script src="https://unpkg.com/classnames/index.js"></script> | ||
<script src="https://unpkg.com/react-input-autosize/dist/react-input-autosize.js"></script> | ||
<script src="https://unpkg.com/react-select/dist/react-select.js"></script> | ||
<link rel="stylesheet" href="https://unpkg.com/react-select/dist/react-select.css"> | ||
``` | ||
## Usage | ||
@@ -63,3 +76,3 @@ | ||
When the value is changed, `onChange(newValue, [selectedOptions])` will fire. | ||
When the value is changed, `onChange(selectedValueOrValues)` will fire. | ||
@@ -91,7 +104,7 @@ ```javascript | ||
* Selected options will be removed from the dropdown menu | ||
* The values of the selected items are joined using the `delimiter` property to create the input value | ||
* A simple value, if provided, will be split using the `delimiter` property | ||
* The `onChange` event provides an array of the selected options as the second argument | ||
* The first argument to `onChange` is always a string, regardless of whether the values of the selected options are numbers or strings | ||
* By default, only options in the `options` array can be selected. Setting `allowCreate` to true allows new options to be created if they do not already exist. | ||
* The selected values are submitted in multiple `<input type="hidden">` fields, use `joinValues` to submit joined values in a single field instead | ||
* The values of the selected items are joined using the `delimiter` prop to create the input value when `joinValues` is true | ||
* A simple value, if provided, will be split using the `delimiter` prop | ||
* The `onChange` event provides an array of selected options _or_ a comma-separated string of values (eg `"1,2,3"`) if `simpleValue` is true | ||
* By default, only options in the `options` array can be selected. Setting `allowCreate` to true allows new options to be created if they do not already exist. *NOTE:* `allowCreate` is not implemented in `1.0.0-beta`, if you need this option please stay on `0.9.x`. | ||
* By default, selected options can be cleared. To disable the possibility of clearing a particular option, add `clearableValue: false` to that option: | ||
@@ -192,2 +205,45 @@ ```javascript | ||
### User-created tags | ||
The `Creatable` component enables users to create new tags within react-select. | ||
It decorates a `Select` and so it supports all of the default properties (eg single/multi mode, filtering, etc) in addition to a couple of custom ones (shown below). | ||
The easiest way to use it is like so: | ||
```js | ||
import { Creatable } from 'react-select'; | ||
function render (selectProps) { | ||
return <Creatable {...selectProps} />; | ||
}; | ||
``` | ||
##### Creatable properties | ||
Property | Type | Description | ||
:---|:---|:--- | ||
`children` | function | Child function responsible for creating the inner Select component. This component can be used to compose HOCs (eg Creatable and Async). Expected signature: `(props: Object): PropTypes.element` | | ||
`isOptionUnique` | function | Searches for any matching option within the set of options. This function prevents duplicate options from being created. By default this is a basic, case-sensitive comparison of label and value. Expected signature: `({ option: Object, options: Array, labelKey: string, valueKey: string }): boolean` | | ||
`isValidNewOption` | function | Determines if the current input text represents a valid option. By default any non-empty string will be considered valid. Expected signature: `({ label: string }): boolean` | | ||
`newOptionCreator` | function | Factory to create new option. Expected signature: `({ label: string, labelKey: string, valueKey: string }): Object` | | ||
`shouldKeyDownEventCreateNewOption` | function | Decides if a keyDown event (eg its `keyCode`) should result in the creation of a new option. ENTER, TAB and comma keys create new options by default. Expected signature: `({ keyCode: number }): boolean` | | ||
`promptTextCreator` | function | Factory for overriding default option creator prompt label. By default it will read 'Create option "{label}"'. Expected signature: `(label: String): String` | | ||
### Combining Async and Creatable | ||
Use the `AsyncCreatable` HOC if you want both _async_ and _creatable_ functionality. | ||
It ties `Async` and `Creatable` components together and supports a union of their properties (listed above). | ||
Use it as follows: | ||
```jsx | ||
import React from 'react'; | ||
import { AsyncCreatable } from 'react-select'; | ||
function render (props) { | ||
// props can be a mix of Async, Creatable, and Select properties | ||
return ( | ||
<AsyncCreatable {...props} /> | ||
); | ||
} | ||
``` | ||
### Filtering options | ||
@@ -213,2 +269,72 @@ | ||
#### Filtering large lists | ||
The default `filterOptions` method scans the options array for matches each time the filter text changes. | ||
This works well but can get slow as the options array grows to several hundred objects. | ||
For larger options lists a custom filter function like [`react-select-fast-filter-options`](https://github.com/bvaughn/react-select-fast-filter-options) will produce better results. | ||
### Effeciently rendering large lists with windowing | ||
The `menuRenderer` property can be used to override the default drop-down list of options. | ||
This should be done when the list is large (hundreds or thousands of items) for faster rendering. | ||
Windowing libraries like [`react-virtualized`](https://github.com/bvaughn/react-virtualized) can then be used to more efficiently render the drop-down menu like so. | ||
The easiest way to do this is with the [`react-virtualized-select`](https://github.com/bvaughn/react-virtualized-select/) HOC. | ||
This component decorates a `Select` and uses the react-virtualized `VirtualScroll` component to render options. | ||
Demo and documentation for this component are available [here](https://bvaughn.github.io/react-virtualized-select/). | ||
You can also specify your own custom renderer. | ||
The custom `menuRenderer` property accepts the following named parameters: | ||
| Parameter | Type | Description | | ||
|:---|:---|:---| | ||
| focusedOption | `Object` | The currently focused option; should be visible in the menu by default. | | ||
| focusOption | `Function` | Callback to focus a new option; receives the option as a parameter. | | ||
| labelKey | `String` | Option labels are accessible with this string key. | | ||
| optionClassName | `String` | The className that gets used for options | | ||
| optionComponent | `ReactClass` | The react component that gets used for rendering an option | | ||
| optionRenderer | `Function` | The function that gets used to render the content of an option | | ||
| options | `Array<Object>` | Ordered array of options to render. | | ||
| selectValue | `Function` | Callback to select a new option; receives the option as a parameter. | | ||
| valueArray | `Array<Object>` | Array of currently selected options. | | ||
### Updating input values with onInputChange | ||
You can manipulate the input using the onInputChange and returning a new value. | ||
```js | ||
function cleanInput(inputValue) { | ||
// Strip all non-number characters from the input | ||
return inputValue.replace(/[^0-9]/g, ""); | ||
} | ||
<Select | ||
name="form-field-name" | ||
onInputChange={cleanInput} | ||
/> | ||
``` | ||
### Overriding default key-down behavior with onInputKeyDown | ||
`Select` listens to `keyDown` events to select items, navigate drop-down list via arrow keys, etc. | ||
You can extend or override this behavior by providing a `onInputKeyDown` callback. | ||
```js | ||
function onInputKeyDown(event) { | ||
switch (event.keyCode) { | ||
case 9: // TAB | ||
// Extend default TAB behavior by doing something here | ||
break; | ||
case 13: // ENTER | ||
// Override default ENTER behavior by doing stuff here and then preventing default | ||
event.preventDefault(); | ||
break; | ||
} | ||
} | ||
<Select | ||
{...otherProps} | ||
onInputKeyDown={onInputKeyDown} | ||
/> | ||
``` | ||
### Further options | ||
@@ -220,7 +346,9 @@ | ||
addLabelText | string | 'Add "{label}"?' | text to display when `allowCreate` is true | ||
autosize | bool | true | If enabled, the input will expand as the length of its value increases | ||
arrowRenderer | func | undefined | Renders a custom drop-down arrow to be shown in the right-hand side of the select: `arrowRenderer({ onMouseDown })` | ||
autoBlur | bool | false | Blurs the input element after a selection has been made. Handy for lowering the keyboard on mobile devices | ||
allowCreate | bool | false | allow new options to be created in multi mode (displays an "Add \<option> ?" item when a value not already in the `options` array is entered) | ||
autofocus | bool | undefined | autofocus the component on mount | ||
autoload | bool | true | whether to auto-load the default async options set | ||
autosize | bool | true | If enabled, the input will expand as the length of its value increases | ||
backspaceRemoves | bool | true | whether pressing backspace removes the last item when there is no input value | ||
backspaceToRemoveMessage | string | 'Press backspace to remove {last label}' | prompt shown in input when at least one option in a multiselect is shown, set to '' to clear | ||
cache | bool | true | enables the options cache for `asyncOptions` (default: `true`) | ||
@@ -231,2 +359,3 @@ className | string | undefined | className for the outer element | ||
clearValueText | string | 'Clear value' | title for the "clear" control | ||
resetValue | any | null | value to use when you clear the control | ||
delimiter | string | ',' | delimiter to use to join multiple values | ||
@@ -244,7 +373,6 @@ disabled | bool | false | whether the Select is disabled or not | ||
matchProp | string | 'any' | (any, label, value) which option property to filter on | ||
scrollMenuIntoView | bool | true | whether the viewport will shift to display the entire menu when engaged | ||
menuBuffer | number | 0 | buffer of px between the base of the dropdown and the viewport to shift if menu doesnt fit in viewport | ||
menuRenderer | func | undefined | Renders a custom menu with options; accepts the following named parameters: `menuRenderer({ focusedOption, focusOption, options, selectValue, valueArray })` | ||
multi | bool | undefined | multi-value input | ||
name | string | undefined | field name, for hidden `<input />` tag | ||
newOptionCreator | func | undefined | factory to create new options when `allowCreate` is true | ||
noResultsText | string | 'No results found' | placeholder displayed when there are no matching search results or a falsy value to hide it | ||
@@ -254,16 +382,20 @@ onBlur | func | undefined | onBlur handler: `function(event) {}` | ||
onChange | func | undefined | onChange handler: `function(newValue) {}` | ||
onClose | func | undefined | handler for when the menu closes: `function () {}` | ||
onCloseResetsInput | bool | true | whether to clear input when closing the menu through the arrow | ||
onFocus | func | undefined | onFocus handler: `function(event) {}` | ||
onInputChange | func | undefined | onInputChange handler: `function(inputValue) {}` | ||
onInputKeyDown | func | undefined | input keyDown handler; call `event.preventDefault()` to override default `Select` behavior: `function(event) {}` | ||
onOpen | func | undefined | handler for when the menu opens: `function () {}` | ||
onValueClick | func | undefined | onClick handler for value labels: `function (value, event) {}` | ||
onOpen | func | undefined | handler for when the menu opens: `function () {}` | ||
onClose | func | undefined | handler for when the menu closes: `function () {}` | ||
openOnFocus | bool | false | open the options menu when the input gets focus (requires searchable = true) | ||
optionRenderer | func | undefined | function which returns a custom way to render the options in the menu | ||
options | array | undefined | array of options | ||
placeholder | string\|node | 'Select ...' | field placeholder, displayed when there's no value | ||
scrollMenuIntoView | bool | true | whether the viewport will shift to display the entire menu when engaged | ||
searchable | bool | true | whether to enable searching feature or not | ||
searchingText | string | 'Searching...' | message to display whilst options are loading via asyncOptions, or when `isLoading` is true | ||
searchPromptText | string | 'Type to search' | label to prompt for search input | ||
searchPromptText | string\|node | 'Type to search' | label to prompt for search input | ||
tabSelectsValue | bool | true | whether to select the currently focused value when the `[tab]` key is pressed | ||
value | any | undefined | initial field value | ||
valueKey | string | 'value' | the option property to use for the value | ||
valueRenderer | func | undefined | function which returns a custom way to render the value selected | ||
valueRenderer | func | undefined | function which returns a custom way to render the value selected `function (option) {}` | ||
@@ -270,0 +402,0 @@ ### Methods |
316
src/Async.js
@@ -1,154 +0,206 @@ | ||
import React from 'react'; | ||
import React, { Component, PropTypes } from 'react'; | ||
import Select from './Select'; | ||
import stripDiacritics from './utils/stripDiacritics'; | ||
let requestId = 0; | ||
const propTypes = { | ||
autoload: React.PropTypes.bool.isRequired, // automatically call the `loadOptions` prop on-mount; defaults to true | ||
cache: React.PropTypes.any, // object to use to cache results; set to null/false to disable caching | ||
children: React.PropTypes.func.isRequired, // Child function responsible for creating the inner Select component; (props: Object): PropTypes.element | ||
ignoreAccents: React.PropTypes.bool, // strip diacritics when filtering; defaults to true | ||
ignoreCase: React.PropTypes.bool, // perform case-insensitive filtering; defaults to true | ||
loadingPlaceholder: React.PropTypes.oneOfType([ // replaces the placeholder while options are loading | ||
React.PropTypes.string, | ||
React.PropTypes.node | ||
]), | ||
loadOptions: React.PropTypes.func.isRequired, // callback to load options asynchronously; (inputValue: string, callback: Function): ?Promise | ||
options: PropTypes.array.isRequired, // array of options | ||
placeholder: React.PropTypes.oneOfType([ // field placeholder, displayed when there's no value (shared with Select) | ||
React.PropTypes.string, | ||
React.PropTypes.node | ||
]), | ||
noResultsText: React.PropTypes.oneOfType([ // field noResultsText, displayed when no options come back from the server | ||
React.PropTypes.string, | ||
React.PropTypes.node | ||
]), | ||
searchPromptText: React.PropTypes.oneOfType([ // label to prompt for search input | ||
React.PropTypes.string, | ||
React.PropTypes.node | ||
]), | ||
onInputChange: React.PropTypes.func, // optional for keeping track of what is being typed | ||
value: React.PropTypes.any, // initial field value | ||
}; | ||
function initCache (cache) { | ||
if (cache && typeof cache !== 'object') { | ||
cache = {}; | ||
const defaultProps = { | ||
autoload: true, | ||
cache: {}, | ||
children: defaultChildren, | ||
ignoreAccents: true, | ||
ignoreCase: true, | ||
loadingPlaceholder: 'Loading...', | ||
options: [], | ||
searchPromptText: 'Type to search', | ||
}; | ||
export default class Async extends Component { | ||
constructor (props, context) { | ||
super(props, context); | ||
this.state = { | ||
isLoading: false, | ||
options: props.options, | ||
}; | ||
this._onInputChange = this._onInputChange.bind(this); | ||
} | ||
return cache ? cache : null; | ||
} | ||
function updateCache (cache, input, data) { | ||
if (!cache) return; | ||
cache[input] = data; | ||
} | ||
componentDidMount () { | ||
const { autoload } = this.props; | ||
function getFromCache (cache, input) { | ||
if (!cache) return; | ||
for (let i = input.length; i >= 0; --i) { | ||
let cacheKey = input.slice(0, i); | ||
if (cache[cacheKey] && (input === cacheKey || cache[cacheKey].complete)) { | ||
return cache[cacheKey]; | ||
if (autoload) { | ||
this.loadOptions(''); | ||
} | ||
} | ||
} | ||
function thenPromise (promise, callback) { | ||
if (!promise || typeof promise.then !== 'function') return; | ||
return promise.then((data) => { | ||
callback(null, data); | ||
}, (err) => { | ||
callback(err); | ||
}); | ||
} | ||
componentWillUpdate (nextProps, nextState) { | ||
const propertiesToSync = ['options']; | ||
propertiesToSync.forEach((prop) => { | ||
if (this.props[prop] !== nextProps[prop]) { | ||
this.setState({ | ||
[prop]: nextProps[prop] | ||
}); | ||
} | ||
}); | ||
} | ||
const stringOrNode = React.PropTypes.oneOfType([ | ||
React.PropTypes.string, | ||
React.PropTypes.node | ||
]); | ||
clearOptions() { | ||
this.setState({ options: [] }); | ||
} | ||
const Async = React.createClass({ | ||
propTypes: { | ||
cache: React.PropTypes.any, // object to use to cache results, can be null to disable cache | ||
ignoreAccents: React.PropTypes.bool, // whether to strip diacritics when filtering (shared with Select) | ||
ignoreCase: React.PropTypes.bool, // whether to perform case-insensitive filtering (shared with Select) | ||
isLoading: React.PropTypes.bool, // overrides the isLoading state when set to true | ||
loadOptions: React.PropTypes.func.isRequired, // function to call to load options asynchronously | ||
loadingPlaceholder: React.PropTypes.string, // replaces the placeholder while options are loading | ||
minimumInput: React.PropTypes.number, // the minimum number of characters that trigger loadOptions | ||
noResultsText: React.PropTypes.string, // placeholder displayed when there are no matching search results (shared with Select) | ||
placeholder: stringOrNode, // field placeholder, displayed when there's no value (shared with Select) | ||
searchPromptText: React.PropTypes.string, // label to prompt for search input | ||
searchingText: React.PropTypes.string, // message to display while options are loading | ||
}, | ||
getDefaultProps () { | ||
return { | ||
cache: true, | ||
ignoreAccents: true, | ||
ignoreCase: true, | ||
loadingPlaceholder: 'Loading...', | ||
minimumInput: 0, | ||
searchingText: 'Searching...', | ||
searchPromptText: 'Type to search', | ||
}; | ||
}, | ||
getInitialState () { | ||
return { | ||
cache: initCache(this.props.cache), | ||
isLoading: false, | ||
options: [], | ||
}; | ||
}, | ||
componentWillMount () { | ||
this._lastInput = ''; | ||
}, | ||
componentDidMount () { | ||
this.loadOptions(''); | ||
}, | ||
componentWillReceiveProps (nextProps) { | ||
if (nextProps.cache !== this.props.cache) { | ||
loadOptions (inputValue) { | ||
const { cache, loadOptions } = this.props; | ||
if ( | ||
cache && | ||
cache.hasOwnProperty(inputValue) | ||
) { | ||
this.setState({ | ||
cache: initCache(nextProps.cache), | ||
options: cache[inputValue] | ||
}); | ||
return; | ||
} | ||
}, | ||
focus () { | ||
this.refs.select.focus(); | ||
}, | ||
resetState () { | ||
this._currentRequestId = -1; | ||
this.setState({ | ||
isLoading: false, | ||
options: [], | ||
}); | ||
}, | ||
getResponseHandler (input) { | ||
let _requestId = this._currentRequestId = requestId++; | ||
return (err, data) => { | ||
if (err) throw err; | ||
if (!this.isMounted()) return; | ||
updateCache(this.state.cache, input, data); | ||
if (_requestId !== this._currentRequestId) return; | ||
this.setState({ | ||
isLoading: false, | ||
options: data && data.options || [], | ||
}); | ||
const callback = (error, data) => { | ||
if (callback === this._callback) { | ||
this._callback = null; | ||
const options = data && data.options || []; | ||
if (cache) { | ||
cache[inputValue] = options; | ||
} | ||
this.setState({ | ||
isLoading: false, | ||
options | ||
}); | ||
} | ||
}; | ||
}, | ||
loadOptions (input) { | ||
if (this.props.ignoreAccents) input = stripDiacritics(input); | ||
if (this.props.ignoreCase) input = input.toLowerCase(); | ||
this._lastInput = input; | ||
if (input.length < this.props.minimumInput) { | ||
return this.resetState(); | ||
// Ignore all but the most recent request | ||
this._callback = callback; | ||
const promise = loadOptions(inputValue, callback); | ||
if (promise) { | ||
promise.then( | ||
(data) => callback(null, data), | ||
(error) => callback(error) | ||
); | ||
} | ||
let cacheResult = getFromCache(this.state.cache, input); | ||
if (cacheResult) { | ||
return this.setState({ | ||
options: cacheResult.options, | ||
if ( | ||
this._callback && | ||
!this.state.isLoading | ||
) { | ||
this.setState({ | ||
isLoading: true | ||
}); | ||
} | ||
this.setState({ | ||
isLoading: true, | ||
}); | ||
let responseHandler = this.getResponseHandler(input); | ||
return thenPromise(this.props.loadOptions(input, responseHandler), responseHandler); | ||
}, | ||
render () { | ||
let { noResultsText } = this.props; | ||
let { isLoading, options } = this.state; | ||
if (this.props.isLoading) isLoading = true; | ||
let placeholder = isLoading ? this.props.loadingPlaceholder : this.props.placeholder; | ||
if (!options.length) { | ||
if (this._lastInput.length < this.props.minimumInput) noResultsText = this.props.searchPromptText; | ||
if (isLoading) noResultsText = this.props.searchingText; | ||
return inputValue; | ||
} | ||
_onInputChange (inputValue) { | ||
const { ignoreAccents, ignoreCase, onInputChange } = this.props; | ||
if (ignoreAccents) { | ||
inputValue = stripDiacritics(inputValue); | ||
} | ||
return ( | ||
<Select | ||
{...this.props} | ||
ref="select" | ||
isLoading={isLoading} | ||
noResultsText={noResultsText} | ||
onInputChange={this.loadOptions} | ||
options={options} | ||
placeholder={placeholder} | ||
/> | ||
); | ||
if (ignoreCase) { | ||
inputValue = inputValue.toLowerCase(); | ||
} | ||
if (onInputChange) { | ||
onInputChange(inputValue); | ||
} | ||
return this.loadOptions(inputValue); | ||
} | ||
}); | ||
module.exports = Async; | ||
inputValue() { | ||
if (this.select) { | ||
return this.select.state.inputValue; | ||
} | ||
return ''; | ||
} | ||
noResultsText() { | ||
const { loadingPlaceholder, noResultsText, searchPromptText } = this.props; | ||
const { isLoading } = this.state; | ||
const inputValue = this.inputValue(); | ||
if (isLoading) { | ||
return loadingPlaceholder; | ||
} | ||
if (inputValue && noResultsText) { | ||
return noResultsText; | ||
} | ||
return searchPromptText; | ||
} | ||
render () { | ||
const { children, loadingPlaceholder, placeholder } = this.props; | ||
const { isLoading, options } = this.state; | ||
const props = { | ||
noResultsText: this.noResultsText(), | ||
placeholder: isLoading ? loadingPlaceholder : placeholder, | ||
options: (isLoading && loadingPlaceholder) ? [] : options, | ||
ref: (ref) => (this.select = ref), | ||
onChange: (newValues) => { | ||
if (this.props.value && (newValues.length > this.props.value.length)) { | ||
this.clearOptions(); | ||
} | ||
this.props.onChange(newValues); | ||
} | ||
}; | ||
return children({ | ||
...this.props, | ||
...props, | ||
isLoading, | ||
onInputChange: this._onInputChange | ||
}); | ||
} | ||
} | ||
Async.propTypes = propTypes; | ||
Async.defaultProps = defaultProps; | ||
function defaultChildren (props) { | ||
return ( | ||
<Select {...props} /> | ||
); | ||
}; |
@@ -8,2 +8,3 @@ import React from 'react'; | ||
className: React.PropTypes.string, // className (based on mouse position) | ||
instancePrefix: React.PropTypes.string.isRequired, // unique prefix for the ids (used for aria) | ||
isDisabled: React.PropTypes.bool, // the option is disabled | ||
@@ -16,2 +17,3 @@ isFocused: React.PropTypes.bool, // the option is focused | ||
option: React.PropTypes.object.isRequired, // object that is base for that option | ||
optionIndex: React.PropTypes.number, // index of the option, used to generate unique ids for aria | ||
}, | ||
@@ -69,3 +71,3 @@ blockEvent (event) { | ||
render () { | ||
var { option } = this.props; | ||
var { option, instancePrefix, optionIndex } = this.props; | ||
var className = classNames(this.props.className, option.className); | ||
@@ -82,3 +84,4 @@ | ||
style={option.style} | ||
onMouseDown={this.handleMouseDown} | ||
role="option" | ||
onMouseDown={this.handleMouseDown} | ||
onMouseEnter={this.handleMouseEnter} | ||
@@ -89,2 +92,3 @@ onMouseMove={this.handleMouseMove} | ||
onTouchEnd={this.handleTouchEnd} | ||
id={instancePrefix + '-option-' + optionIndex} | ||
title={option.title}> | ||
@@ -91,0 +95,0 @@ {this.props.children} |
@@ -0,9 +1,19 @@ | ||
/*! | ||
Copyright (c) 2016 Jed Watson. | ||
Licensed under the MIT License (MIT), see | ||
http://jedwatson.github.io/react-select | ||
*/ | ||
import React from 'react'; | ||
import ReactDOM from 'react-dom'; | ||
import Input from 'react-input-autosize'; | ||
import AutosizeInput from 'react-input-autosize'; | ||
import classNames from 'classnames'; | ||
import stripDiacritics from './utils/stripDiacritics'; | ||
import defaultArrowRenderer from './utils/defaultArrowRenderer'; | ||
import defaultFilterOptions from './utils/defaultFilterOptions'; | ||
import defaultMenuRenderer from './utils/defaultMenuRenderer'; | ||
import Async from './Async'; | ||
import AsyncCreatable from './AsyncCreatable'; | ||
import Creatable from './Creatable'; | ||
import Option from './Option'; | ||
@@ -13,6 +23,11 @@ import Value from './Value'; | ||
function stringifyValue (value) { | ||
if (typeof value === 'object') { | ||
const valueType = typeof value; | ||
if (valueType === 'string') { | ||
return value; | ||
} else if (valueType === 'object') { | ||
return JSON.stringify(value); | ||
} else if (valueType === 'number' || valueType === 'boolean') { | ||
return String(value); | ||
} else { | ||
return value; | ||
return ''; | ||
} | ||
@@ -26,2 +41,4 @@ } | ||
let instanceId = 1; | ||
const Select = React.createClass({ | ||
@@ -33,7 +50,10 @@ | ||
addLabelText: React.PropTypes.string, // placeholder displayed when you want to add a label on a multi-value input | ||
autosize: React.PropTypes.bool, // whether to enable autosizing or not | ||
allowCreate: React.PropTypes.bool, // whether to allow creation of new entries | ||
autoBlur: React.PropTypes.bool, | ||
'aria-label': React.PropTypes.string, // Aria label (for assistive tech) | ||
'aria-labelledby': React.PropTypes.string, // HTML ID of an element that should be used as the label (for assistive tech) | ||
arrowRenderer: React.PropTypes.func, // Create drop-down caret element | ||
autoBlur: React.PropTypes.bool, // automatically blur the component when an option is selected | ||
autofocus: React.PropTypes.bool, // autofocus the component on mount | ||
autosize: React.PropTypes.bool, // whether to enable autosizing or not | ||
backspaceRemoves: React.PropTypes.bool, // whether backspace removes an item if there is no text input | ||
backspaceToRemoveMessage: React.PropTypes.string, // Message to use for screenreaders to press backspace to remove the current item - {label} is replaced with the item label | ||
className: React.PropTypes.string, // className for the outer element | ||
@@ -52,2 +72,4 @@ clearAllText: stringOrNode, // title for the "clear" control when multi: true | ||
inputProps: React.PropTypes.object, // custom attributes for the Input | ||
inputRenderer: React.PropTypes.func, // returns a custom input component | ||
instanceId: React.PropTypes.string, // set the components instanceId | ||
isLoading: React.PropTypes.bool, // whether the Select is loading externally or not (such as options being loaded) | ||
@@ -60,6 +82,6 @@ joinValues: React.PropTypes.bool, // joins multiple values into a single form field with the delimiter (legacy mode) | ||
menuContainerStyle: React.PropTypes.object, // optional style to apply to the menu container | ||
menuRenderer: React.PropTypes.func, // renders a custom menu with options | ||
menuStyle: React.PropTypes.object, // optional style to apply to the menu | ||
multi: React.PropTypes.bool, // multi-value input | ||
name: React.PropTypes.string, // generates a hidden <input /> tag with this field name for html forms | ||
newOptionCreator: React.PropTypes.func, // factory to create new options when allowCreate set | ||
noResultsText: stringOrNode, // placeholder displayed when there are no matching search results | ||
@@ -70,12 +92,19 @@ onBlur: React.PropTypes.func, // onBlur handler: function (event) {} | ||
onClose: React.PropTypes.func, // fires when the menu is closed | ||
onCloseResetsInput: React.PropTypes.bool, // whether input is cleared when menu is closed through the arrow | ||
onFocus: React.PropTypes.func, // onFocus handler: function (event) {} | ||
onInputChange: React.PropTypes.func, // onInputChange handler: function (inputValue) {} | ||
onInputKeyDown: React.PropTypes.func, // input keyDown handler: function (event) {} | ||
onMenuScrollToBottom: React.PropTypes.func, // fires when the menu is scrolled to the bottom; can be used to paginate options | ||
onOpen: React.PropTypes.func, // fires when the menu is opened | ||
onValueClick: React.PropTypes.func, // onClick handler for value labels: function (value, event) {} | ||
openAfterFocus: React.PropTypes.bool, // boolean to enable opening dropdown when focused | ||
openOnFocus: React.PropTypes.bool, // always open options menu on focus | ||
optionClassName: React.PropTypes.string, // additional class(es) to apply to the <Option /> elements | ||
optionComponent: React.PropTypes.func, // option component to render in dropdown | ||
optionRenderer: React.PropTypes.func, // optionRenderer: function (option) {} | ||
options: React.PropTypes.array, // array of options | ||
pageSize: React.PropTypes.number, // number of entries to page when using page up/down keys | ||
placeholder: stringOrNode, // field placeholder, displayed when there's no value | ||
required: React.PropTypes.bool, // applies HTML5 required attribute when needed | ||
resetValue: React.PropTypes.any, // value to use when you clear the control | ||
scrollMenuIntoView: React.PropTypes.bool, // boolean to enable the viewport to shift so that the full menu fully visible when engaged | ||
@@ -86,2 +115,3 @@ searchable: React.PropTypes.bool, // whether to enable searching feature or not | ||
tabIndex: React.PropTypes.string, // optional tab index of the control | ||
tabSelectsValue: React.PropTypes.bool, // whether to treat tabbing out while focused to be value selection | ||
value: React.PropTypes.any, // initial field value | ||
@@ -92,5 +122,6 @@ valueComponent: React.PropTypes.func, // value component to render | ||
wrapperStyle: React.PropTypes.object, // optional style to apply to the component wrapper | ||
inputValue: React.PropTypes.string, | ||
}, | ||
statics: { Async }, | ||
statics: { Async, AsyncCreatable, Creatable }, | ||
@@ -100,5 +131,6 @@ getDefaultProps () { | ||
addLabelText: 'Add "{label}"?', | ||
arrowRenderer: defaultArrowRenderer, | ||
autosize: true, | ||
allowCreate: false, | ||
backspaceRemoves: true, | ||
backspaceToRemoveMessage: 'Press backspace to remove {label}', | ||
clearable: true, | ||
@@ -110,3 +142,3 @@ clearAllText: 'Clear all', | ||
escapeClearsValue: true, | ||
filterOptions: true, | ||
filterOptions: defaultFilterOptions, | ||
ignoreAccents: true, | ||
@@ -121,6 +153,10 @@ ignoreCase: true, | ||
menuBuffer: 0, | ||
menuRenderer: defaultMenuRenderer, | ||
multi: false, | ||
noResultsText: 'No results found', | ||
onBlurResetsInput: true, | ||
onCloseResetsInput: true, | ||
openAfterFocus: false, | ||
optionComponent: Option, | ||
pageSize: 5, | ||
placeholder: 'Select...', | ||
@@ -131,4 +167,6 @@ required: false, | ||
simpleValue: false, | ||
tabSelectsValue: true, | ||
valueComponent: Value, | ||
valueKey: 'value', | ||
inputValue: '', | ||
}; | ||
@@ -139,11 +177,21 @@ }, | ||
return { | ||
inputValue: '', | ||
inputValue: this.props.inputValue, | ||
isFocused: false, | ||
isLoading: false, | ||
isOpen: false, | ||
isPseudoFocused: false, | ||
required: this.props.required && this.handleRequired(this.props.value, this.props.multi) | ||
required: false, | ||
}; | ||
}, | ||
componentWillMount() { | ||
this._instancePrefix = 'react-select-' + (this.props.instanceId || ++instanceId) + '-'; | ||
const valueArray = this.getValueArray(this.props.value); | ||
if (this.props.required) { | ||
this.setState({ | ||
required: this.handleRequired(valueArray[0], this.props.multi), | ||
}); | ||
} | ||
}, | ||
componentDidMount () { | ||
@@ -156,7 +204,12 @@ if (this.props.autofocus) { | ||
componentWillReceiveProps(nextProps) { | ||
if (this.props.value !== nextProps.value && nextProps.required) { | ||
const valueArray = this.getValueArray(nextProps.value, nextProps); | ||
if (nextProps.required) { | ||
this.setState({ | ||
required: this.handleRequired(nextProps.value, nextProps.multi), | ||
required: this.handleRequired(valueArray[0], nextProps.multi), | ||
}); | ||
} | ||
if (nextProps.inputValue !== '') { | ||
this.setState({ inputValue: nextProps.inputValue }); | ||
} | ||
}, | ||
@@ -166,2 +219,3 @@ | ||
if (nextState.isOpen !== this.state.isOpen) { | ||
this.toggleTouchOutsideEvent(nextState.isOpen); | ||
const handler = nextState.isOpen ? nextProps.onOpen : nextProps.onClose; | ||
@@ -174,5 +228,5 @@ handler && handler(); | ||
// focus to the selected option | ||
if (this.refs.menu && this.refs.focused && this.state.isOpen && !this.hasScrolledToOption) { | ||
let focusedOptionNode = ReactDOM.findDOMNode(this.refs.focused); | ||
let menuNode = ReactDOM.findDOMNode(this.refs.menu); | ||
if (this.menu && this.focused && this.state.isOpen && !this.hasScrolledToOption) { | ||
let focusedOptionNode = ReactDOM.findDOMNode(this.focused); | ||
let menuNode = ReactDOM.findDOMNode(this.menu); | ||
menuNode.scrollTop = focusedOptionNode.offsetTop; | ||
@@ -184,9 +238,6 @@ this.hasScrolledToOption = true; | ||
if (prevState.inputValue !== this.state.inputValue && this.props.onInputChange) { | ||
this.props.onInputChange(this.state.inputValue); | ||
} | ||
if (this._scrollToFocusedOptionOnUpdate && this.refs.focused && this.refs.menu) { | ||
if (this._scrollToFocusedOptionOnUpdate && this.focused && this.menu) { | ||
this._scrollToFocusedOptionOnUpdate = false; | ||
var focusedDOM = ReactDOM.findDOMNode(this.refs.focused); | ||
var menuDOM = ReactDOM.findDOMNode(this.refs.menu); | ||
var focusedDOM = ReactDOM.findDOMNode(this.focused); | ||
var menuDOM = ReactDOM.findDOMNode(this.menu); | ||
var focusedRect = focusedDOM.getBoundingClientRect(); | ||
@@ -198,6 +249,6 @@ var menuRect = menuDOM.getBoundingClientRect(); | ||
} | ||
if (this.props.scrollMenuIntoView && this.refs.menuContainer) { | ||
var menuContainerRect = this.refs.menuContainer.getBoundingClientRect(); | ||
if (this.props.scrollMenuIntoView && this.menuContainer) { | ||
var menuContainerRect = this.menuContainer.getBoundingClientRect(); | ||
if (window.innerHeight < menuContainerRect.bottom + this.props.menuBuffer) { | ||
window.scrollTo(0, window.scrollY + menuContainerRect.bottom + this.props.menuBuffer - window.innerHeight); | ||
window.scrollBy(0, menuContainerRect.bottom + this.props.menuBuffer - window.innerHeight); | ||
} | ||
@@ -207,13 +258,51 @@ } | ||
this.setState({ isFocused: false }); // eslint-disable-line react/no-did-update-set-state | ||
this.closeMenu(); | ||
} | ||
}, | ||
componentWillUnmount() { | ||
if (!document.removeEventListener && document.detachEvent) { | ||
document.detachEvent('ontouchstart', this.handleTouchOutside); | ||
} else { | ||
document.removeEventListener('touchstart', this.handleTouchOutside); | ||
} | ||
}, | ||
toggleTouchOutsideEvent(enabled) { | ||
if (enabled) { | ||
if (!document.addEventListener && document.attachEvent) { | ||
document.attachEvent('ontouchstart', this.handleTouchOutside); | ||
} else { | ||
document.addEventListener('touchstart', this.handleTouchOutside); | ||
} | ||
} else { | ||
if (!document.removeEventListener && document.detachEvent) { | ||
document.detachEvent('ontouchstart', this.handleTouchOutside); | ||
} else { | ||
document.removeEventListener('touchstart', this.handleTouchOutside); | ||
} | ||
} | ||
}, | ||
handleTouchOutside(event) { | ||
// handle touch outside on ios to dismiss menu | ||
if (this.wrapper && !this.wrapper.contains(event.target)) { | ||
this.closeMenu(); | ||
} | ||
}, | ||
focus () { | ||
if (!this.refs.input) return; | ||
this.refs.input.focus(); | ||
if (!this.input) return; | ||
this.input.focus(); | ||
if (this.props.openAfterFocus) { | ||
this.setState({ | ||
isOpen: true, | ||
}); | ||
} | ||
}, | ||
blurInput() { | ||
if (!this.refs.input) return; | ||
this.refs.input.blur(); | ||
if (!this.input) return; | ||
this.input.blur(); | ||
}, | ||
@@ -256,2 +345,6 @@ | ||
if (event.target.tagName === 'INPUT') { | ||
return; | ||
} | ||
if (!this.state.isOpen) { | ||
@@ -271,2 +364,16 @@ event.preventDefault(); | ||
if (this.state.isFocused) { | ||
// On iOS, we can get into a state where we think the input is focused but it isn't really, | ||
// since iOS ignores programmatic calls to input.focus() that weren't triggered by a click event. | ||
// Call focus() again here to be safe. | ||
this.focus(); | ||
let input = this.input; | ||
if (typeof input.getInput === 'function') { | ||
// Get the actual DOM input if the ref is an <AutosizeInput /> component | ||
input = input.getInput(); | ||
} | ||
// clears the value so that the cursor will be at the end of input when the component re-renders | ||
input.value = ''; | ||
// if the input is focused, ensure the menu is open | ||
@@ -304,4 +411,4 @@ this.setState({ | ||
// button, or if the component is disabled, ignore it. | ||
if (this.props.disabled || (event.type === 'mousedown' && event.button !== 0)) { | ||
return; | ||
if (this.props.disabled || (event.type === 'mousedown' && event.button !== 0)) { | ||
return; | ||
} | ||
@@ -316,7 +423,15 @@ event.stopPropagation(); | ||
closeMenu () { | ||
this.setState({ | ||
isOpen: false, | ||
isPseudoFocused: this.state.isFocused && !this.props.multi, | ||
inputValue: '', | ||
}); | ||
if(this.props.onCloseResetsInput) { | ||
this.setState({ | ||
isOpen: false, | ||
isPseudoFocused: this.state.isFocused && !this.props.multi, | ||
inputValue: '' | ||
}); | ||
} else { | ||
this.setState({ | ||
isOpen: false, | ||
isPseudoFocused: this.state.isFocused && !this.props.multi, | ||
inputValue: this.state.inputValue | ||
}); | ||
} | ||
this.hasScrolledToOption = false; | ||
@@ -326,3 +441,4 @@ }, | ||
handleInputFocus (event) { | ||
var isOpen = this.state.isOpen || this._openAfterFocus; | ||
if (this.props.disabled) return; | ||
var isOpen = this.state.isOpen || this._openAfterFocus || this.props.openOnFocus; | ||
if (this.props.onFocus) { | ||
@@ -343,5 +459,7 @@ this.props.onFocus(event); | ||
handleInputBlur (event) { | ||
if (this.refs.menu && document.activeElement.isEqualNode(this.refs.menu)) { | ||
return; | ||
} | ||
// The check for menu.contains(activeElement) is necessary to prevent IE11's scrollbar from closing the menu in certain contexts. | ||
if (this.menu && (this.menu === document.activeElement || this.menu.contains(document.activeElement))) { | ||
this.focus(); | ||
return; | ||
} | ||
@@ -363,6 +481,16 @@ if (this.props.onBlur) { | ||
handleInputChange (event) { | ||
let newInputValue = event.target.value; | ||
if (this.state.inputValue !== event.target.value && this.props.onInputChange) { | ||
let nextState = this.props.onInputChange(newInputValue); | ||
// Note: != used deliberately here to catch undefined and null | ||
if (nextState != null && typeof nextState !== 'object') { | ||
newInputValue = '' + nextState; | ||
} | ||
} | ||
this.setState({ | ||
isOpen: true, | ||
isPseudoFocused: false, | ||
inputValue: event.target.value, | ||
inputValue: newInputValue | ||
}); | ||
@@ -373,2 +501,10 @@ }, | ||
if (this.props.disabled) return; | ||
if (typeof this.props.onInputKeyDown === 'function') { | ||
this.props.onInputKeyDown(event); | ||
if (event.defaultPrevented) { | ||
return; | ||
} | ||
} | ||
switch (event.keyCode) { | ||
@@ -382,3 +518,3 @@ case 8: // backspace | ||
case 9: // tab | ||
if (event.shiftKey || !this.state.isOpen) { | ||
if (event.shiftKey || !this.state.isOpen || !this.props.tabSelectsValue) { | ||
return; | ||
@@ -396,4 +532,6 @@ } | ||
this.closeMenu(); | ||
event.stopPropagation(); | ||
} else if (this.props.clearable && this.props.escapeClearsValue) { | ||
this.clearValue(event); | ||
event.stopPropagation(); | ||
} | ||
@@ -407,11 +545,20 @@ break; | ||
break; | ||
// case 188: // , | ||
// if (this.props.allowCreate && this.props.multi) { | ||
// event.preventDefault(); | ||
// event.stopPropagation(); | ||
// this.selectFocusedOption(); | ||
// } else { | ||
// return; | ||
// } | ||
// break; | ||
case 33: // page up | ||
this.focusPageUpOption(); | ||
break; | ||
case 34: // page down | ||
this.focusPageDownOption(); | ||
break; | ||
case 35: // end key | ||
if (event.shiftKey) { | ||
return; | ||
} | ||
this.focusEndOption(); | ||
break; | ||
case 36: // home key | ||
if (event.shiftKey) { | ||
return; | ||
} | ||
this.focusStartOption(); | ||
break; | ||
default: return; | ||
@@ -444,6 +591,13 @@ } | ||
getValueArray () { | ||
let value = this.props.value; | ||
if (this.props.multi) { | ||
if (typeof value === 'string') value = value.split(this.props.delimiter); | ||
/** | ||
* Turns a value into an array from the given options | ||
* @param {String|Number|Array} value - the value of the select input | ||
* @param {Object} nextProps - optionally specify the nextProps so the returned array uses the latest configuration | ||
* @returns {Array} the value of the select represented in an array | ||
*/ | ||
getValueArray (value, nextProps) { | ||
/** support optionally passing in the `nextProps` so `componentWillReceiveProps` updates will function as expected */ | ||
const props = typeof nextProps === 'object' ? nextProps : this.props; | ||
if (props.multi) { | ||
if (typeof value === 'string') value = value.split(props.delimiter); | ||
if (!Array.isArray(value)) { | ||
@@ -453,11 +607,17 @@ if (value === null || value === undefined) return []; | ||
} | ||
return value.map(this.expandValue).filter(i => i); | ||
return value.map(value => this.expandValue(value, props)).filter(i => i); | ||
} | ||
var expandedValue = this.expandValue(value); | ||
var expandedValue = this.expandValue(value, props); | ||
return expandedValue ? [expandedValue] : []; | ||
}, | ||
expandValue (value) { | ||
if (typeof value !== 'string' && typeof value !== 'number') return value; | ||
let { options, valueKey } = this.props; | ||
/** | ||
* Retrieve a value from the given options and valueKey | ||
* @param {String|Number|Array} value - the selected value(s) | ||
* @param {Object} props - the Select component's props (or nextProps) | ||
*/ | ||
expandValue (value, props) { | ||
const valueType = typeof value; | ||
if (valueType !== 'string' && valueType !== 'number' && valueType !== 'boolean') return value; | ||
let { options, valueKey } = props; | ||
if (!options) return; | ||
@@ -485,10 +645,12 @@ for (var i = 0; i < options.length; i++) { | ||
selectValue (value) { | ||
//NOTE: update value in the callback to make sure the input value is empty so that there are no styling issues (Chrome had issue otherwise) | ||
this.hasScrolledToOption = false; | ||
if (this.props.multi) { | ||
this.addValue(value); | ||
this.setState({ | ||
inputValue: '', | ||
focusedIndex: null | ||
}, () => { | ||
this.addValue(value); | ||
}); | ||
} else { | ||
this.setValue(value); | ||
this.setState({ | ||
@@ -498,2 +660,4 @@ isOpen: false, | ||
isPseudoFocused: this.state.isFocused, | ||
}, () => { | ||
this.setValue(value); | ||
}); | ||
@@ -504,3 +668,3 @@ } | ||
addValue (value) { | ||
var valueArray = this.getValueArray(); | ||
var valueArray = this.getValueArray(this.props.value); | ||
this.setValue(valueArray.concat(value)); | ||
@@ -510,3 +674,3 @@ }, | ||
popValue () { | ||
var valueArray = this.getValueArray(); | ||
var valueArray = this.getValueArray(this.props.value); | ||
if (!valueArray.length) return; | ||
@@ -518,3 +682,3 @@ if (valueArray[valueArray.length-1].clearableValue === false) return; | ||
removeValue (value) { | ||
var valueArray = this.getValueArray(); | ||
var valueArray = this.getValueArray(this.props.value); | ||
this.setValue(valueArray.filter(i => i !== value)); | ||
@@ -532,3 +696,3 @@ this.focus(); | ||
event.preventDefault(); | ||
this.setValue(null); | ||
this.setValue(this.getResetValue()); | ||
this.setState({ | ||
@@ -540,2 +704,12 @@ isOpen: false, | ||
getResetValue() { | ||
if (this.props.resetValue !== undefined) { | ||
return this.props.resetValue; | ||
} else if (this.props.multi) { | ||
return []; | ||
} else { | ||
return null; | ||
} | ||
}, | ||
focusOption (option) { | ||
@@ -555,4 +729,22 @@ this.setState({ | ||
focusPageUpOption () { | ||
this.focusAdjacentOption('page_up'); | ||
}, | ||
focusPageDownOption () { | ||
this.focusAdjacentOption('page_down'); | ||
}, | ||
focusStartOption () { | ||
this.focusAdjacentOption('start'); | ||
}, | ||
focusEndOption () { | ||
this.focusAdjacentOption('end'); | ||
}, | ||
focusAdjacentOption (dir) { | ||
var options = this._visibleOptions.filter(i => !i.disabled); | ||
var options = this._visibleOptions | ||
.map((option, index) => ({ option, index })) | ||
.filter(option => !option.option.disabled); | ||
this._scrollToFocusedOptionOnUpdate = true; | ||
@@ -563,3 +755,3 @@ if (!this.state.isOpen) { | ||
inputValue: '', | ||
focusedOption: this._focusedOption || options[dir === 'next' ? 0 : options.length - 1] | ||
focusedOption: this._focusedOption || options[dir === 'next' ? 0 : options.length - 1].option | ||
}); | ||
@@ -571,3 +763,3 @@ return; | ||
for (var i = 0; i < options.length; i++) { | ||
if (this._focusedOption === options[i]) { | ||
if (this._focusedOption === options[i].option) { | ||
focusedIndex = i; | ||
@@ -577,17 +769,48 @@ break; | ||
} | ||
var focusedOption = options[0]; | ||
if (dir === 'next' && focusedIndex > -1 && focusedIndex < options.length - 1) { | ||
focusedOption = options[focusedIndex + 1]; | ||
if (dir === 'next' && focusedIndex !== -1 ) { | ||
focusedIndex = (focusedIndex + 1) % options.length; | ||
} else if (dir === 'previous') { | ||
if (focusedIndex > 0) { | ||
focusedOption = options[focusedIndex - 1]; | ||
focusedIndex = focusedIndex - 1; | ||
} else { | ||
focusedOption = options[options.length - 1]; | ||
focusedIndex = options.length - 1; | ||
} | ||
} else if (dir === 'start') { | ||
focusedIndex = 0; | ||
} else if (dir === 'end') { | ||
focusedIndex = options.length - 1; | ||
} else if (dir === 'page_up') { | ||
var potentialIndex = focusedIndex - this.props.pageSize; | ||
if ( potentialIndex < 0 ) { | ||
focusedIndex = 0; | ||
} else { | ||
focusedIndex = potentialIndex; | ||
} | ||
} else if (dir === 'page_down') { | ||
var potentialIndex = focusedIndex + this.props.pageSize; | ||
if ( potentialIndex > options.length - 1 ) { | ||
focusedIndex = options.length - 1; | ||
} else { | ||
focusedIndex = potentialIndex; | ||
} | ||
} | ||
if (focusedIndex === -1) { | ||
focusedIndex = 0; | ||
} | ||
this.setState({ | ||
focusedOption: focusedOption | ||
focusedIndex: options[focusedIndex].index, | ||
focusedOption: options[focusedIndex].option | ||
}); | ||
}, | ||
getFocusedOption () { | ||
return this._focusedOption; | ||
}, | ||
getInputValue () { | ||
return this.state.inputValue; | ||
}, | ||
selectFocusedOption () { | ||
@@ -624,2 +847,4 @@ var options = this.filterOptions(null); | ||
<ValueComponent | ||
id={this._instancePrefix + '-value-' + i} | ||
instancePrefix={this._instancePrefix} | ||
disabled={this.props.disabled || value.clearableValue === false} | ||
@@ -630,4 +855,5 @@ key={`value-${i}-${value[this.props.valueKey]}`} | ||
value={value} | ||
> | ||
{renderLabel(value)} | ||
> | ||
{renderLabel(value, i)} | ||
<span className="Select-aria-only"> </span> | ||
</ValueComponent> | ||
@@ -640,6 +866,8 @@ ); | ||
<ValueComponent | ||
id={this._instancePrefix + '-value-item'} | ||
disabled={this.props.disabled} | ||
instancePrefix={this._instancePrefix} | ||
onClick={onClick} | ||
value={valueArray[0]} | ||
> | ||
> | ||
{renderLabel(valueArray[0])} | ||
@@ -651,57 +879,78 @@ </ValueComponent> | ||
renderInput (valueArray) { | ||
var className = classNames('Select-input', this.props.inputProps.className); | ||
if (this.props.disabled || !this.props.searchable) { | ||
renderInput (valueArray, focusedOptionIndex) { | ||
if (this.props.inputRenderer) { | ||
return this.props.inputRenderer(); | ||
} else { | ||
var className = classNames('Select-input', this.props.inputProps.className); | ||
const isOpen = !!this.state.isOpen; | ||
const ariaOwns = classNames({ | ||
[this._instancePrefix + '-list']: isOpen, | ||
[this._instancePrefix + '-backspace-remove-message']: this.props.multi | ||
&& !this.props.disabled | ||
&& this.state.isFocused | ||
&& !this.state.inputValue | ||
}); | ||
// TODO: Check how this project includes Object.assign() | ||
const inputProps = Object.assign({}, this.props.inputProps, { | ||
role: 'combobox', | ||
'aria-expanded': '' + isOpen, | ||
'aria-owns': ariaOwns, | ||
'aria-haspopup': '' + isOpen, | ||
'aria-activedescendant': isOpen ? this._instancePrefix + '-option-' + focusedOptionIndex : this._instancePrefix + '-value', | ||
'aria-labelledby': this.props['aria-labelledby'], | ||
'aria-label': this.props['aria-label'], | ||
className: className, | ||
tabIndex: this.props.tabIndex, | ||
onBlur: this.handleInputBlur, | ||
onChange: this.handleInputChange, | ||
onFocus: this.handleInputFocus, | ||
ref: ref => this.input = ref, | ||
required: this.state.required, | ||
value: this.state.inputValue | ||
}); | ||
if (this.props.disabled || !this.props.searchable) { | ||
const { inputClassName, ...divProps } = this.props.inputProps; | ||
return ( | ||
<div | ||
{...divProps} | ||
role="combobox" | ||
aria-expanded={isOpen} | ||
aria-owns={isOpen ? this._instancePrefix + '-list' : this._instancePrefix + '-value'} | ||
aria-activedescendant={isOpen ? this._instancePrefix + '-option-' + focusedOptionIndex : this._instancePrefix + '-value'} | ||
className={className} | ||
tabIndex={this.props.tabIndex || 0} | ||
onBlur={this.handleInputBlur} | ||
onFocus={this.handleInputFocus} | ||
ref={ref => this.input = ref} | ||
aria-readonly={'' + !!this.props.disabled} | ||
style={{ border: 0, width: 1, display:'inline-block' }}/> | ||
); | ||
} | ||
if (this.props.autosize) { | ||
return ( | ||
<AutosizeInput {...inputProps} minWidth="5" /> | ||
); | ||
} | ||
return ( | ||
<div | ||
{...this.props.inputProps} | ||
className={className} | ||
tabIndex={this.props.tabIndex || 0} | ||
onBlur={this.handleInputBlur} | ||
onFocus={this.handleInputFocus} | ||
ref="input" | ||
style={{ border: 0, width: 1, display:'inline-block' }}/> | ||
<div className={ className }> | ||
<input {...inputProps} /> | ||
</div> | ||
); | ||
} | ||
if (this.props.autosize) { | ||
return ( | ||
<Input | ||
{...this.props.inputProps} | ||
className={className} | ||
tabIndex={this.props.tabIndex} | ||
onBlur={this.handleInputBlur} | ||
onChange={this.handleInputChange} | ||
onFocus={this.handleInputFocus} | ||
minWidth="5" | ||
ref="input" | ||
required={this.state.required} | ||
value={this.state.inputValue} | ||
/> | ||
); | ||
} | ||
return ( | ||
<div className={ className }> | ||
<input | ||
{...this.props.inputProps} | ||
tabIndex={this.props.tabIndex} | ||
onBlur={this.handleInputBlur} | ||
onChange={this.handleInputChange} | ||
onFocus={this.handleInputFocus} | ||
ref="input" | ||
required={this.state.required} | ||
value={this.state.inputValue} | ||
/> | ||
</div> | ||
); | ||
}, | ||
renderClear () { | ||
if (!this.props.clearable || !this.props.value || (this.props.multi && !this.props.value.length) || this.props.disabled || this.props.isLoading) return; | ||
if (!this.props.clearable || (!this.props.value || this.props.value === 0) || (this.props.multi && !this.props.value.length) || this.props.disabled || this.props.isLoading) return; | ||
return ( | ||
<span className="Select-clear-zone" title={this.props.multi ? this.props.clearAllText : this.props.clearValueText} | ||
aria-label={this.props.multi ? this.props.clearAllText : this.props.clearValueText} | ||
onMouseDown={this.clearValue} | ||
onTouchStart={this.handleTouchStart} | ||
onTouchMove={this.handleTouchMove} | ||
onTouchEnd={this.handleTouchEndClearValue}> | ||
aria-label={this.props.multi ? this.props.clearAllText : this.props.clearValueText} | ||
onMouseDown={this.clearValue} | ||
onTouchStart={this.handleTouchStart} | ||
onTouchMove={this.handleTouchMove} | ||
onTouchEnd={this.handleTouchEndClearValue} | ||
> | ||
<span className="Select-clear" dangerouslySetInnerHTML={{ __html: '×' }} /> | ||
@@ -713,5 +962,11 @@ </span> | ||
renderArrow () { | ||
const onMouseDown = this.handleMouseDownOnArrow; | ||
const arrow = this.props.arrowRenderer({ onMouseDown }); | ||
return ( | ||
<span className="Select-arrow-zone" onMouseDown={this.handleMouseDownOnArrow}> | ||
<span className="Select-arrow" onMouseDown={this.handleMouseDownOnArrow} /> | ||
<span | ||
className="Select-arrow-zone" | ||
onMouseDown={onMouseDown} | ||
> | ||
{arrow} | ||
</span> | ||
@@ -724,34 +979,22 @@ ); | ||
var options = this.props.options || []; | ||
if (typeof this.props.filterOptions === 'function') { | ||
return this.props.filterOptions.call(this, options, filterValue, excludeOptions); | ||
} else if (this.props.filterOptions) { | ||
if (this.props.ignoreAccents) { | ||
filterValue = stripDiacritics(filterValue); | ||
} | ||
if (this.props.ignoreCase) { | ||
filterValue = filterValue.toLowerCase(); | ||
} | ||
if (excludeOptions) excludeOptions = excludeOptions.map(i => i[this.props.valueKey]); | ||
return options.filter(option => { | ||
if (excludeOptions && excludeOptions.indexOf(option[this.props.valueKey]) > -1) return false; | ||
if (this.props.filterOption) return this.props.filterOption.call(this, option, filterValue); | ||
if (!filterValue) return true; | ||
var valueTest = String(option[this.props.valueKey]); | ||
var labelTest = String(option[this.props.labelKey]); | ||
if (this.props.ignoreAccents) { | ||
if (this.props.matchProp !== 'label') valueTest = stripDiacritics(valueTest); | ||
if (this.props.matchProp !== 'value') labelTest = stripDiacritics(labelTest); | ||
if (this.props.filterOptions) { | ||
// Maintain backwards compatibility with boolean attribute | ||
const filterOptions = typeof this.props.filterOptions === 'function' | ||
? this.props.filterOptions | ||
: defaultFilterOptions; | ||
return filterOptions( | ||
options, | ||
filterValue, | ||
excludeOptions, | ||
{ | ||
filterOption: this.props.filterOption, | ||
ignoreAccents: this.props.ignoreAccents, | ||
ignoreCase: this.props.ignoreCase, | ||
labelKey: this.props.labelKey, | ||
matchPos: this.props.matchPos, | ||
matchProp: this.props.matchProp, | ||
valueKey: this.props.valueKey, | ||
} | ||
if (this.props.ignoreCase) { | ||
if (this.props.matchProp !== 'label') valueTest = valueTest.toLowerCase(); | ||
if (this.props.matchProp !== 'value') labelTest = labelTest.toLowerCase(); | ||
} | ||
return 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) | ||
); | ||
}); | ||
); | ||
} else { | ||
@@ -762,33 +1005,25 @@ return options; | ||
onOptionRef(ref, isFocused) { | ||
if (isFocused) { | ||
this.focused = ref; | ||
} | ||
}, | ||
renderMenu (options, valueArray, focusedOption) { | ||
if (options && options.length) { | ||
let Option = this.props.optionComponent; | ||
let renderLabel = this.props.optionRenderer || this.getOptionLabel; | ||
return options.map((option, i) => { | ||
let isSelected = valueArray && valueArray.indexOf(option) > -1; | ||
let isFocused = option === focusedOption; | ||
let optionRef = isFocused ? 'focused' : null; | ||
let optionClass = classNames({ | ||
'Select-option': true, | ||
'is-selected': isSelected, | ||
'is-focused': isFocused, | ||
'is-disabled': option.disabled, | ||
}); | ||
return ( | ||
<Option | ||
className={optionClass} | ||
isDisabled={option.disabled} | ||
isFocused={isFocused} | ||
key={`option-${i}-${option[this.props.valueKey]}`} | ||
onSelect={this.selectValue} | ||
onFocus={this.focusOption} | ||
option={option} | ||
isSelected={isSelected} | ||
ref={optionRef} | ||
> | ||
{renderLabel(option)} | ||
</Option> | ||
); | ||
return this.props.menuRenderer({ | ||
focusedOption, | ||
focusOption: this.focusOption, | ||
instancePrefix: this._instancePrefix, | ||
labelKey: this.props.labelKey, | ||
onFocus: this.focusOption, | ||
onSelect: this.selectValue, | ||
optionClassName: this.props.optionClassName, | ||
optionComponent: this.props.optionComponent, | ||
optionRenderer: this.props.optionRenderer || this.getOptionLabel, | ||
options, | ||
selectValue: this.selectValue, | ||
valueArray, | ||
valueKey: this.props.valueKey, | ||
onOptionRef: this.onOptionRef, | ||
}); | ||
@@ -813,3 +1048,3 @@ } else if (this.props.noResultsText) { | ||
type="hidden" | ||
ref="value" | ||
ref={ref => this.value = ref} | ||
name={this.props.name} | ||
@@ -830,20 +1065,54 @@ value={value} | ||
getFocusableOption (selectedOption) { | ||
getFocusableOptionIndex (selectedOption) { | ||
var options = this._visibleOptions; | ||
if (!options.length) return; | ||
if (!options.length) return null; | ||
let focusedOption = this.state.focusedOption || selectedOption; | ||
if (focusedOption && options.indexOf(focusedOption) > -1) return focusedOption; | ||
if (focusedOption && !focusedOption.disabled) { | ||
const focusedOptionIndex = options.indexOf(focusedOption); | ||
if (focusedOptionIndex !== -1) { | ||
return focusedOptionIndex; | ||
} | ||
} | ||
for (var i = 0; i < options.length; i++) { | ||
if (!options[i].disabled) return options[i]; | ||
if (!options[i].disabled) return i; | ||
} | ||
return null; | ||
}, | ||
renderOuter (options, valueArray, focusedOption) { | ||
let menu = this.renderMenu(options, valueArray, focusedOption); | ||
if (!menu) { | ||
return null; | ||
} | ||
return ( | ||
<div ref={ref => this.menuContainer = ref} className="Select-menu-outer" style={this.props.menuContainerStyle}> | ||
<div ref={ref => this.menu = ref} role="listbox" className="Select-menu" id={this._instancePrefix + '-list'} | ||
style={this.props.menuStyle} | ||
onScroll={this.handleMenuScroll} | ||
onMouseDown={this.handleMouseDownOnMenu}> | ||
{menu} | ||
</div> | ||
</div> | ||
); | ||
}, | ||
render () { | ||
let valueArray = this.getValueArray(); | ||
let options = this._visibleOptions = this.filterOptions(this.props.multi ? valueArray : null); | ||
let valueArray = this.getValueArray(this.props.value); | ||
let options = this._visibleOptions = this.filterOptions(this.props.multi ? this.getValueArray(this.props.value) : null); | ||
let isOpen = this.state.isOpen; | ||
if (this.props.multi && !options.length && valueArray.length && !this.state.inputValue) isOpen = false; | ||
let focusedOption = this._focusedOption = this.getFocusableOption(valueArray[0]); | ||
const focusedOptionIndex = this.getFocusableOptionIndex(valueArray[0]); | ||
let focusedOption = null; | ||
if (focusedOptionIndex !== null) { | ||
focusedOption = this._focusedOption = options[focusedOptionIndex]; | ||
} else { | ||
focusedOption = this._focusedOption = null; | ||
} | ||
let className = classNames('Select', this.props.className, { | ||
'Select--multi': this.props.multi, | ||
'Select--single': !this.props.multi, | ||
'is-disabled': this.props.disabled, | ||
@@ -857,15 +1126,36 @@ 'is-focused': this.state.isFocused, | ||
}); | ||
let removeMessage = null; | ||
if (this.props.multi && | ||
!this.props.disabled && | ||
valueArray.length && | ||
!this.state.inputValue && | ||
this.state.isFocused && | ||
this.props.backspaceRemoves) { | ||
removeMessage = ( | ||
<span id={this._instancePrefix + '-backspace-remove-message'} className="Select-aria-only" aria-live="assertive"> | ||
{this.props.backspaceToRemoveMessage.replace('{label}', valueArray[valueArray.length - 1][this.props.labelKey])} | ||
</span> | ||
); | ||
} | ||
return ( | ||
<div ref="wrapper" className={className} style={this.props.wrapperStyle}> | ||
<div ref={ref => this.wrapper = ref} | ||
className={className} | ||
style={this.props.wrapperStyle}> | ||
{this.renderHiddenField(valueArray)} | ||
<div ref="control" | ||
className="Select-control" | ||
style={this.props.style} | ||
onKeyDown={this.handleKeyDown} | ||
onMouseDown={this.handleMouseDown} | ||
onTouchEnd={this.handleTouchEnd} | ||
onTouchStart={this.handleTouchStart} | ||
onTouchMove={this.handleTouchMove}> | ||
{this.renderValue(valueArray, isOpen)} | ||
{this.renderInput(valueArray)} | ||
<div ref={ref => this.control = ref} | ||
className="Select-control" | ||
style={this.props.style} | ||
onKeyDown={this.handleKeyDown} | ||
onMouseDown={this.handleMouseDown} | ||
onTouchEnd={this.handleTouchEnd} | ||
onTouchStart={this.handleTouchStart} | ||
onTouchMove={this.handleTouchMove} | ||
> | ||
<span className="Select-multi-value-wrapper" id={this._instancePrefix + '-value'}> | ||
{this.renderValue(valueArray, isOpen)} | ||
{this.renderInput(valueArray, focusedOptionIndex)} | ||
</span> | ||
{removeMessage} | ||
{this.renderLoading()} | ||
@@ -875,12 +1165,3 @@ {this.renderClear()} | ||
</div> | ||
{isOpen ? ( | ||
<div ref="menuContainer" className="Select-menu-outer" style={this.props.menuContainerStyle}> | ||
<div ref="menu" className="Select-menu" | ||
style={this.props.menuStyle} | ||
onScroll={this.handleMenuScroll} | ||
onMouseDown={this.handleMouseDownOnMenu}> | ||
{this.renderMenu(options, !this.props.multi ? valueArray : null, focusedOption)} | ||
</div> | ||
</div> | ||
) : null} | ||
{isOpen ? this.renderOuter(options, !this.props.multi ? valueArray : null, focusedOption) : null} | ||
</div> | ||
@@ -887,0 +1168,0 @@ ); |
@@ -11,2 +11,3 @@ import React from 'react'; | ||
disabled: React.PropTypes.bool, // disabled prop passed to ReactSelect | ||
id: React.PropTypes.string, // Unique id for the value - used for aria | ||
onClick: React.PropTypes.func, // method to handle click on value label | ||
@@ -60,2 +61,3 @@ onRemove: React.PropTypes.func, // method to handle removal of the value | ||
<span className="Select-value-icon" | ||
aria-hidden="true" | ||
onMouseDown={this.onRemove} | ||
@@ -77,3 +79,3 @@ onTouchEnd={this.handleTouchEndRemove} | ||
) : ( | ||
<span className={className}> | ||
<span className={className} role="option" aria-selected="true" id={this.props.id}> | ||
{this.props.children} | ||
@@ -89,5 +91,7 @@ </span> | ||
title={this.props.value.title} | ||
> | ||
{this.renderRemoveIcon()} | ||
{this.renderLabel()} | ||
> | ||
<div className='Select-value-inner'> | ||
{this.renderRemoveIcon()} | ||
{this.renderLabel()} | ||
</div> | ||
</div> | ||
@@ -94,0 +98,0 @@ ); |
@@ -23,47 +23,104 @@ 'use strict'; | ||
var Select = require('../src/Select'); | ||
describe('Async', () => { | ||
let asyncInstance, asyncNode, filterInputNode, loadOptions; | ||
var renderer; | ||
var loadOptions; | ||
const typeSearchText = function(text) { | ||
const output = renderer.getRenderOutput(); | ||
// onInputChange is actually bound to loadOptions(...), | ||
// loadOptions returns the promise, if there is one, so we can use this | ||
// to chain the assertions onto | ||
return output.props.onInputChange(text); | ||
function createControl (props = {}) { | ||
loadOptions = props.loadOptions || sinon.stub(); | ||
asyncInstance = TestUtils.renderIntoDocument( | ||
<Select.Async | ||
autoload={false} | ||
openOnFocus | ||
{...props} | ||
loadOptions={loadOptions} | ||
/> | ||
); | ||
asyncNode = ReactDOM.findDOMNode(asyncInstance); | ||
findAndFocusInputControl(); | ||
}; | ||
beforeEach(() => { | ||
function createOptionsResponse (options) { | ||
return { | ||
options: options.map((option) => ({ | ||
label: option, | ||
value: option | ||
})) | ||
}; | ||
} | ||
renderer = TestUtils.createRenderer(); | ||
loadOptions = sinon.stub(); | ||
}); | ||
function findAndFocusInputControl () { | ||
filterInputNode = asyncNode.querySelector('input'); | ||
if (filterInputNode) { | ||
TestUtils.Simulate.focus(filterInputNode); | ||
} | ||
}; | ||
describe('using promises', () => { | ||
function typeSearchText (text) { | ||
TestUtils.Simulate.change(filterInputNode, { target: { value: text } }); | ||
}; | ||
describe('autoload', () => { | ||
it('false does not call loadOptions on-mount', () => { | ||
createControl({ | ||
autoload: false | ||
}); | ||
expect(loadOptions, 'was not called'); | ||
}); | ||
beforeEach(() => { | ||
it('true calls loadOptions on-mount', () => { | ||
createControl({ | ||
autoload: true | ||
}); | ||
expect(loadOptions, 'was called'); | ||
}); | ||
}); | ||
// Render an instance of the component | ||
renderer.render(<Select.Async loadOptions={loadOptions} minimumInput={1}/>); | ||
describe('cache', () => { | ||
it('should be used instead of loadOptions if input has been previously loaded', () => { | ||
createControl(); | ||
typeSearchText('a'); | ||
return expect(loadOptions, 'was called times', 1); | ||
typeSearchText('b'); | ||
return expect(loadOptions, 'was called times', 2); | ||
typeSearchText('a'); | ||
return expect(loadOptions, 'was called times', 2); | ||
typeSearchText('b'); | ||
return expect(loadOptions, 'was called times', 2); | ||
typeSearchText('c'); | ||
return expect(loadOptions, 'was called times', 3); | ||
}); | ||
it('does not call loadOptions', () => { | ||
expect(loadOptions, 'was not called'); | ||
it('can be disabled by passing null/false', () => { | ||
createControl({ | ||
cache: false | ||
}); | ||
typeSearchText('a'); | ||
return expect(loadOptions, 'was called times', 1); | ||
typeSearchText('b'); | ||
return expect(loadOptions, 'was called times', 2); | ||
typeSearchText('a'); | ||
return expect(loadOptions, 'was called times', 3); | ||
typeSearchText('b'); | ||
return expect(loadOptions, 'was called times', 4); | ||
}); | ||
it('renders the select with no options', () => { | ||
return expect(renderer, 'to have rendered', | ||
<Select options={ [] } noResultsText="Type to search"/>); | ||
it('can be customized', () => { | ||
createControl({ | ||
cache: { | ||
a: [] | ||
} | ||
}); | ||
typeSearchText('a'); | ||
return expect(loadOptions, 'was called times', 0); | ||
typeSearchText('b'); | ||
return expect(loadOptions, 'was called times', 1); | ||
typeSearchText('a'); | ||
return expect(loadOptions, 'was called times', 1); | ||
}); | ||
}); | ||
it('calls the loadOptions for each input', () => { | ||
describe('loadOptions', () => { | ||
it('calls the loadOptions when search input text changes', () => { | ||
createControl(); | ||
typeSearchText('te'); | ||
@@ -75,390 +132,264 @@ typeSearchText('tes'); | ||
it('shows "Loading..." after typing', () => { | ||
it('shows the loadingPlaceholder text while options are being fetched', () => { | ||
function loadOptions (input, callback) {} | ||
createControl({ | ||
loadOptions, | ||
loadingPlaceholder: 'Loading' | ||
}); | ||
typeSearchText('te'); | ||
return expect(renderer, 'to have rendered', | ||
<Select | ||
isLoading | ||
placeholder="Loading..." | ||
noResultsText="Searching..." | ||
/>); | ||
return expect(asyncNode.textContent, 'to contain', 'Loading'); | ||
}); | ||
it('shows the returns options when the result returns', () => { | ||
// Unexpected comes with promises built in - we'll use them | ||
// rather than depending on another library | ||
loadOptions.returns(expect.promise((resolve, reject) => { | ||
resolve({ options: [{ value: 1, label: 'test' }] }); | ||
})); | ||
typeSearchText('te'); | ||
return loadOptions.firstCall.returnValue.then(() => { | ||
return expect(renderer, 'to have rendered', | ||
<Select | ||
isLoading={false} | ||
placeholder="Select..." | ||
noResultsText="No results found" | ||
options={ [ { value: 1, label: 'test' } ] } | ||
/>); | ||
describe('with callbacks', () => { | ||
it('should display the loaded options', () => { | ||
function loadOptions (input, resolve) { | ||
resolve(null, createOptionsResponse(['foo'])); | ||
} | ||
createControl({ | ||
cache: false, | ||
loadOptions | ||
}); | ||
expect(asyncNode.querySelectorAll('[role=option]').length, 'to equal', 0); | ||
typeSearchText('foo'); | ||
expect(asyncNode.querySelectorAll('[role=option]').length, 'to equal', 1); | ||
expect(asyncNode.querySelector('[role=option]').textContent, 'to equal', 'foo'); | ||
}); | ||
}); | ||
it('ignores the result of an earlier call, when the responses come in the wrong order', () => { | ||
let promise1Resolve, promise2Resolve; | ||
const promise1 = expect.promise((resolve, reject) => { | ||
promise1Resolve = resolve; | ||
it('should display the most recently-requested loaded options (if results are returned out of order)', () => { | ||
const callbacks = []; | ||
function loadOptions (input, callback) { | ||
callbacks.push(callback); | ||
} | ||
createControl({ | ||
cache: false, | ||
loadOptions | ||
}); | ||
typeSearchText('foo'); | ||
typeSearchText('bar'); | ||
expect(asyncNode.querySelectorAll('[role=option]').length, 'to equal', 0); | ||
callbacks[1](null, createOptionsResponse(['bar'])); | ||
callbacks[0](null, createOptionsResponse(['foo'])); | ||
expect(asyncNode.querySelectorAll('[role=option]').length, 'to equal', 1); | ||
expect(asyncNode.querySelector('[role=option]').textContent, 'to equal', 'bar'); | ||
}); | ||
const promise2 = expect.promise((resolve, reject) => { | ||
promise2Resolve = resolve; | ||
}); | ||
loadOptions.withArgs('te').returns(promise1); | ||
loadOptions.withArgs('tes').returns(promise2); | ||
const firstResult = typeSearchText('te'); | ||
const secondResult = typeSearchText('tes'); | ||
promise2Resolve({ options: [ { value: 2, label: 'test' } ] }); | ||
promise1Resolve({ options: [ { value: 1, label: 'test to ignore' } ] }); | ||
return expect.promise.all([firstResult, secondResult]).then(() => { | ||
return expect(renderer, 'to have rendered', | ||
<Select | ||
isLoading={false} | ||
placeholder="Select..." | ||
noResultsText="No results found" | ||
options={ [ { value: 2, label: 'test' } ] } | ||
/>); | ||
it('should handle an error by setting options to an empty array', () => { | ||
function loadOptions (input, resolve) { | ||
resolve(new Error('error')); | ||
} | ||
createControl({ | ||
cache: false, | ||
loadOptions, | ||
options: createOptionsResponse(['foo']).options | ||
}); | ||
expect(asyncNode.querySelectorAll('[role=option]').length, 'to equal', 1); | ||
typeSearchText('bar'); | ||
expect(asyncNode.querySelectorAll('[role=option]').length, 'to equal', 0); | ||
}); | ||
}); | ||
it('treats a rejected promise as empty options', () => { | ||
let promise1Resolve, promise2Reject; | ||
const promise1 = expect.promise((resolve, reject) => { | ||
promise1Resolve = resolve; | ||
describe('with promises', () => { | ||
it('should display the loaded options', () => { | ||
let promise; | ||
function loadOptions (input) { | ||
promise = expect.promise((resolve, reject) => { | ||
resolve(createOptionsResponse(['foo'])); | ||
}); | ||
return promise; | ||
} | ||
createControl({ | ||
autoload: false, | ||
cache: false, | ||
loadOptions | ||
}); | ||
expect(asyncNode.querySelectorAll('[role=option]').length, 'to equal', 0); | ||
typeSearchText('foo'); | ||
return expect.promise.all([promise]) | ||
.then(() => expect(asyncNode.querySelectorAll('[role=option]').length, 'to equal', 1)) | ||
.then(() => expect(asyncNode.querySelector('[role=option]').textContent, 'to equal', 'foo')); | ||
}); | ||
const promise2 = expect.promise((resolve, reject) => { | ||
promise2Reject = reject; | ||
}); | ||
loadOptions.withArgs('te').returns(promise1); | ||
loadOptions.withArgs('tes').returns(promise2); | ||
const result1 = typeSearchText('te'); | ||
const result2 = typeSearchText('tes'); | ||
promise1Resolve({ options: [ { value: 1, label: 'from te input' }] }); | ||
promise2Reject(); | ||
return expect.promise.all([ result1, result2]).then(() => { | ||
// Previous results (from 'te') are thrown away, and we render with an empty options list | ||
return expect(renderer, 'to have rendered', <Select options={ [] }/>); | ||
}); | ||
}); | ||
}); | ||
describe('with an isLoading prop', () => { | ||
beforeEach(() => { | ||
// Render an instance of the component | ||
renderer.render(<Select.Async | ||
loadOptions={loadOptions} | ||
minimumInput={1} | ||
isLoading | ||
/>); | ||
}); | ||
it('forces the isLoading prop on Select', () => { | ||
return expect(renderer, 'to have rendered', | ||
<Select | ||
isLoading | ||
noResultsText="Searching..." | ||
placeholder="Loading..." | ||
/>); | ||
}); | ||
}); | ||
describe('with a cache', () => { | ||
beforeEach(() => { | ||
// Render an instance of the component | ||
renderer.render(<Select.Async | ||
loadOptions={loadOptions} | ||
minimumInput={1} | ||
cache={ {} } | ||
/>); | ||
}); | ||
it('caches previous responses', () => { | ||
loadOptions.withArgs('te').returns(expect.promise((resolve,reject) => { | ||
resolve({ options: [{ value: 'te', label: 'test 1' }] }); | ||
})); | ||
loadOptions.withArgs('tes').returns(expect.promise((resolve,reject) => { | ||
resolve({ options: [{ value: 'tes', label: 'test 2' } ] }); | ||
})); | ||
// Need to wait for the results be returned, otherwise the cache won't be used | ||
return typeSearchText('te') | ||
.then(() => typeSearchText('tes')) | ||
.then(() => typeSearchText('te')) // this should use the cache | ||
.then(() => { | ||
return expect(loadOptions, 'was called times', 2); | ||
it('should display the most recently-requested loaded options (if results are returned out of order)', () => { | ||
createControl({ | ||
autoload: false, | ||
cache: false | ||
}); | ||
}); | ||
it('does not call `loadOptions` for a longer input, after a `complete=true` response', () => { | ||
loadOptions.withArgs('te').returns(expect.promise((resolve,reject) => { | ||
resolve({ | ||
complete: true, | ||
options: [{ value: 'te', label: 'test 1' }] | ||
let resolveFoo, resolveBar; | ||
const promiseFoo = expect.promise((resolve, reject) => { | ||
resolveFoo = resolve; | ||
}); | ||
})); | ||
return typeSearchText('te') | ||
.then(() => typeSearchText('tes')) | ||
.then(() => { | ||
return expect(loadOptions, 'was called once'); | ||
const promiseBar = expect.promise((resolve, reject) => { | ||
resolveBar = resolve; | ||
}); | ||
}); | ||
loadOptions.withArgs('foo').returns(promiseFoo); | ||
loadOptions.withArgs('bar').returns(promiseBar); | ||
typeSearchText('foo'); | ||
typeSearchText('bar'); | ||
expect(asyncNode.querySelectorAll('[role=option]').length, 'to equal', 0); | ||
resolveBar(createOptionsResponse(['bar'])); | ||
resolveFoo(createOptionsResponse(['foo'])); | ||
return expect.promise.all([promiseFoo, promiseBar]) | ||
.then(() => expect(asyncNode.querySelectorAll('[role=option]').length, 'to equal', 1)) | ||
.then(() => expect(asyncNode.querySelector('[role=option]').textContent, 'to equal', 'bar')); | ||
}); | ||
it('updates the cache when the cache is reset as a prop', () => { | ||
renderer.render(<Select.Async | ||
loadOptions={loadOptions} | ||
minimumInput={1} | ||
cache={ { 'tes': { options: [ { value: 1, label: 'updated cache' } ] } } } | ||
/>); | ||
typeSearchText('tes'); | ||
expect(loadOptions, 'was not called'); | ||
return expect(renderer, 'to have rendered', | ||
<Select options={ [ { value: 1, label: 'updated cache' } ] } /> | ||
); | ||
}); | ||
it('uses the longest complete response if the responses come out-of-order', () => { | ||
let resolveTe, resolveTes; | ||
const promises = []; | ||
promises.push(loadOptions.withArgs('te').returns(expect.promise((resolve, reject) => { | ||
resolveTe = () => resolve({ | ||
complete: true, | ||
options: [ { value: 'te', label: 'test 1' } ] | ||
it('should handle an error by setting options to an empty array', () => { | ||
let promise, rejectPromise; | ||
function loadOptions (input, resolve) { | ||
promise = expect.promise((resolve, reject) => { | ||
rejectPromise = reject; | ||
}); | ||
return promise; | ||
} | ||
createControl({ | ||
autoload: false, | ||
cache: false, | ||
loadOptions, | ||
options: createOptionsResponse(['foo']).options | ||
}); | ||
}))); | ||
promises.push(loadOptions.withArgs('tes').returns(expect.promise((resolve, reject) => { | ||
resolveTes = () => resolve({ | ||
complete: true, | ||
options: [ { value: 'tes', label: 'test 2' } ] | ||
}); | ||
}))); | ||
/* Order is: | ||
* 1. User types 'te' | ||
* 2. loadOptions is called with 'te' | ||
* 3. User types 'tes' | ||
* 4. loadOptions is called with 'tes' | ||
* 5. Response for 'tes' comes back, with { complete: true } | ||
* 6. Response for 'te' comes back, with { complete: true } | ||
* 7. User types 'test' | ||
* 8. Expect that we get the results for 'tes' as options, not 'te' | ||
*/ | ||
typeSearchText('te'); | ||
typeSearchText('tes'); | ||
resolveTes(); | ||
resolveTe(); | ||
return expect.promise.all(promises).then(() => { | ||
typeSearchText('test'); | ||
expect(loadOptions, 'was called times', 2); | ||
return expect(renderer, 'to have rendered', <Select options={ | ||
[ { value: 'tes', label: 'test 2' }] | ||
} />); | ||
expect(asyncNode.querySelectorAll('[role=option]').length, 'to equal', 1); | ||
typeSearchText('bar'); | ||
rejectPromise(new Error('error')); | ||
return expect.promise.all([promise]) | ||
.catch(() => expect(asyncNode.querySelectorAll('[role=option]').length, 'to equal', 0)); | ||
}); | ||
}); | ||
}); | ||
describe('using callbacks', () => { | ||
beforeEach(() => { | ||
// Render an instance of the component | ||
renderer.render(<Select.Async loadOptions={loadOptions} minimumInput={1}/>); | ||
describe('with ignoreAccents', () => { | ||
it('calls loadOptions with unchanged text', () => { | ||
createControl({ | ||
ignoreAccents: true, | ||
ignoreCase: false | ||
}); | ||
typeSearchText('TeSt'); | ||
expect(loadOptions, 'was called with', 'TeSt'); | ||
}); | ||
it('shows the returns options when the result returns', () => { | ||
typeSearchText('te'); | ||
expect(loadOptions, 'was called'); | ||
const callback = loadOptions.args[0][1]; | ||
callback(null, { options: [ { value: 1, label: 'test callback' } ] }); | ||
return expect(renderer, 'to have rendered', | ||
<Select | ||
isLoading={false} | ||
placeholder="Select..." | ||
noResultsText="No results found" | ||
options={ [ { value: 1, label: 'test callback' } ] } | ||
/>); | ||
it('strips accents before calling loadOptions when enabled', () => { | ||
createControl({ | ||
ignoreAccents: true, | ||
ignoreCase: false | ||
}); | ||
typeSearchText('Gedünstmaßig'); | ||
// This should really be Gedunstmassig: ß -> ss | ||
expect(loadOptions, 'was called with', 'Gedunstmasig'); | ||
}); | ||
it('renders results for each callback', () => { | ||
typeSearchText('te'); | ||
typeSearchText('tes'); | ||
expect(loadOptions, 'was called times', 2); | ||
const callback1 = loadOptions.args[0][1]; | ||
const callback2 = loadOptions.args[1][1]; | ||
callback1(null, { options: [ { value: 1, label: 'test callback' } ] }); | ||
callback2(null, { options: [ { value: 2, label: 'test callback 2' } ] }); | ||
return expect(renderer, 'to have rendered', | ||
<Select | ||
isLoading={false} | ||
placeholder="Select..." | ||
noResultsText="No results found" | ||
options={ [ { value: 2, label: 'test callback 2' } ] } | ||
/>); | ||
it('does not strip accents before calling loadOptions when diabled', () => { | ||
createControl({ | ||
ignoreAccents: false, | ||
ignoreCase: false | ||
}); | ||
typeSearchText('Gedünstmaßig'); | ||
expect(loadOptions, 'was called with', 'Gedünstmaßig'); | ||
}); | ||
}); | ||
it('ignores callbacks from earlier requests (out-of-order responses)', () => { | ||
typeSearchText('te'); | ||
typeSearchText('tes'); | ||
expect(loadOptions, 'was called times', 2); | ||
const callback1 = loadOptions.args[0][1]; | ||
const callback2 = loadOptions.args[1][1]; | ||
callback2(null, { options: [ { value: 2, label: 'test callback 2' } ] }); | ||
// Callback1 should be ignored | ||
callback1(null, { options: [ { value: 1, label: 'test callback' } ] }); | ||
return expect(renderer, 'to have rendered', | ||
<Select | ||
isLoading={false} | ||
placeholder="Select..." | ||
noResultsText="No results found" | ||
options={ [ { value: 2, label: 'test callback 2' } ] } | ||
/>); | ||
describe('with ignore case', () => { | ||
it('converts everything to lowercase when enabled', () => { | ||
createControl({ | ||
ignoreAccents: false, | ||
ignoreCase: true | ||
}); | ||
typeSearchText('TeSt'); | ||
expect(loadOptions, 'was called with', 'test'); | ||
}); | ||
it('uses the cache when available', () => { | ||
renderer.render(<Select.Async cache={true} loadOptions={loadOptions} />); | ||
typeSearchText('te'); | ||
loadOptions.args[0][1](null, { options: [ { value: 1, label: 'test1' } ] }); | ||
typeSearchText('tes'); | ||
loadOptions.args[1][1](null, { options: [ { value: 2, label: 'test2' } ] }); | ||
typeSearchText('te'); | ||
expect(loadOptions, 'was called times', 2); | ||
return expect(renderer, 'to have rendered', | ||
<Select options={ [ { value: 1, label: 'test1' } ] } />); | ||
it('converts accents to lowercase when enabled', () => { | ||
createControl({ | ||
ignoreAccents: false, | ||
ignoreCase: true | ||
}); | ||
typeSearchText('WÄRE'); | ||
expect(loadOptions, 'was called with', 'wäre'); | ||
}); | ||
it('throws an error when the callback returns an error', () => { | ||
typeSearchText('te'); | ||
expect(() => loadOptions.args[0][1](new Error('Something went wrong')), | ||
'to throw', 'Something went wrong'); | ||
it('does not convert text to lowercase when disabled', () => { | ||
createControl({ | ||
ignoreAccents: false, | ||
ignoreCase: false | ||
}); | ||
typeSearchText('WÄRE'); | ||
expect(loadOptions, 'was called with', 'WÄRE'); | ||
}); | ||
it('assumes no options when the result has no `options` property', () => { | ||
typeSearchText('te'); | ||
loadOptions.args[0][1](null, [ { value: 1, label: 'should be wrapped in an object' } ] ); | ||
return expect(renderer, 'to have rendered', <Select options={ [] } />); | ||
}); | ||
}); | ||
describe('with minimumInput', () => { | ||
describe('=0', () => { | ||
beforeEach(() => { | ||
renderer.render(<Select.Async loadOptions={loadOptions} minimumInput={0} cache={false} />); | ||
describe('with ignore case and ignore accents', () => { | ||
it('converts everything to lowercase', () => { | ||
createControl({ | ||
ignoreAccents: true, | ||
ignoreCase: true | ||
}); | ||
typeSearchText('TeSt'); | ||
expect(loadOptions, 'was called with', 'test'); | ||
}); | ||
it('calls loadOptions immediately', () => { | ||
// componentDidMount is not currently called with the shallowRenderer, so we need to fake it here | ||
// When we upgrade to 0.15, we'll be able to use renderer.getMountedInstance() | ||
// (or, componentDidMount will be called with the shallow renderer) | ||
renderer._instance._instance.componentDidMount(); | ||
expect(loadOptions, 'was called'); | ||
it('removes accents and converts to lowercase', () => { | ||
createControl({ | ||
ignoreAccents: true, | ||
ignoreCase: true | ||
}); | ||
typeSearchText('WÄRE'); | ||
expect(loadOptions, 'was called with', 'ware'); | ||
}); | ||
}); | ||
it('calls loadOptions again when input returns to empty', () => { | ||
describe('noResultsText', () => { | ||
renderer._instance._instance.componentDidMount(); | ||
typeSearchText('t'); | ||
typeSearchText(''); | ||
expect(loadOptions, 'was called times', 3); | ||
beforeEach(() => { | ||
createControl({ | ||
searchPromptText: 'searchPromptText', | ||
loadingPlaceholder: 'loadingPlaceholder', | ||
noResultsText: 'noResultsText', | ||
}); | ||
}); | ||
describe('=3', () => { | ||
beforeEach(() => { | ||
renderer.render(<Select.Async loadOptions={loadOptions} minimumInput={3} cache={false} />); | ||
describe('before the user inputs text', () => { | ||
it('returns the searchPromptText', () => { | ||
expect(asyncInstance.noResultsText(), 'to equal', 'searchPromptText'); | ||
}); | ||
}); | ||
it('does not call loadOptions initially', () => { | ||
renderer._instance._instance.componentDidMount(); | ||
expect(loadOptions, 'was not called'); | ||
describe('while results are loading', () => { | ||
beforeEach((cb) => { | ||
asyncInstance.setState({ | ||
isLoading: true, | ||
}, cb); | ||
}); | ||
it('returns the loading indicator', () => { | ||
asyncInstance.select = { state: { inputValue: 'asdf' } }; | ||
expect(asyncInstance.noResultsText(), 'to equal', 'loadingPlaceholder'); | ||
}); | ||
}); | ||
it('does not call loadOptions when 2 characters are entered', () => { | ||
typeSearchText('te'); | ||
expect(loadOptions, 'was not called'); | ||
describe('after an empty result set loads', () => { | ||
beforeEach((cb) => { | ||
asyncInstance.setState({ | ||
isLoading: false, | ||
}, cb); | ||
}); | ||
it('calls loadOptions when 3 characters are entered', () => { | ||
typeSearchText('tes'); | ||
expect(loadOptions, 'was called'); | ||
describe('if noResultsText has been provided', () => { | ||
it('returns the noResultsText', () => { | ||
asyncInstance.select = { state: { inputValue: 'asdf' } }; | ||
expect(asyncInstance.noResultsText(), 'to equal', 'noResultsText'); | ||
}); | ||
}); | ||
it('resets to `type to search` when text returns to < 3', () => { | ||
typeSearchText('tes'); | ||
loadOptions.args[0][1](null, { options: [ { value: 1, label: 'test1' } ] }); | ||
typeSearchText('te'); | ||
return expect(renderer, 'to have rendered', | ||
<Select | ||
options={ [] } | ||
placeholder="Select..." | ||
noResultsText="Type to search" | ||
/>); | ||
describe('if noResultsText is empty', () => { | ||
beforeEach((cb) => { | ||
createControl({ | ||
searchPromptText: 'searchPromptText', | ||
loadingPlaceholder: 'loadingPlaceholder' | ||
}); | ||
asyncInstance.setState({ | ||
isLoading: false, | ||
inputValue: 'asdfkljhadsf' | ||
}, cb); | ||
}); | ||
it('falls back to searchPromptText', () => { | ||
asyncInstance.select = { state: { inputValue: 'asdf' } }; | ||
expect(asyncInstance.noResultsText(), 'to equal', 'searchPromptText'); | ||
}); | ||
}); | ||
@@ -468,72 +399,34 @@ }); | ||
describe('with ignoreAccents', () => { | ||
beforeEach(() => { | ||
renderer.render(<Select.Async | ||
loadOptions={loadOptions} | ||
ignoreAccents={true} | ||
ignoreCase={false} | ||
cache={false} />); | ||
describe('children function', () => { | ||
it('should allow a custom select type to be rendered', () => { | ||
let childProps; | ||
createControl({ | ||
autoload: true, | ||
children: (props) => { | ||
childProps = props; | ||
return ( | ||
<div>faux select</div> | ||
); | ||
} | ||
}); | ||
expect(asyncNode.textContent, 'to equal', 'faux select'); | ||
expect(childProps.isLoading, 'to equal', true); | ||
}); | ||
it('calls loadOptions with unchanged text', () => { | ||
typeSearchText('TeSt'); | ||
expect(loadOptions, 'was called with', 'TeSt'); | ||
it('should render a Select component by default', () => { | ||
createControl(); | ||
expect(asyncNode.className, 'to contain', 'Select'); | ||
}); | ||
it('calls loadOptions with accents stripped', () => { | ||
typeSearchText('Gedünstmaßig'); | ||
// This should really be Gedunstmassig: ß -> ss | ||
expect(loadOptions, 'was called with', 'Gedunstmasig'); | ||
}); | ||
}); | ||
describe('with ignore case', () => { | ||
beforeEach(() => { | ||
renderer.render(<Select.Async | ||
loadOptions={loadOptions} | ||
ignoreAccents={false} | ||
ignoreCase={true} | ||
cache={false} />); | ||
describe('with onInputChange', () => { | ||
it('should call onInputChange', () => { | ||
const onInputChange = sinon.stub(); | ||
createControl({ | ||
onInputChange, | ||
}); | ||
typeSearchText('a'); | ||
return expect(onInputChange, 'was called times', 1); | ||
}); | ||
it('converts everything to lowercase', () => { | ||
typeSearchText('TeSt'); | ||
expect(loadOptions, 'was called with', 'test'); | ||
}); | ||
it('converts accents to lowercase', () => { | ||
typeSearchText('WÄRE'); | ||
expect(loadOptions, 'was called with', 'wäre'); | ||
}); | ||
}); | ||
describe('with ignore case and ignore accents', () => { | ||
beforeEach(() => { | ||
renderer.render(<Select.Async | ||
loadOptions={loadOptions} | ||
ignoreAccents={true} | ||
ignoreCase={true} | ||
cache={false} />); | ||
}); | ||
it('converts everything to lowercase', () => { | ||
typeSearchText('TeSt'); | ||
expect(loadOptions, 'was called with', 'test'); | ||
}); | ||
it('removes accents and converts to lowercase', () => { | ||
typeSearchText('WÄRE'); | ||
expect(loadOptions, 'was called with', 'ware'); | ||
}); | ||
}); | ||
}); |
@@ -86,3 +86,3 @@ 'use strict'; | ||
it('calls a custom callback when the anchor is clicked', function() { | ||
TestUtils.Simulate.mouseDown(valueLabel); | ||
TestUtils.Simulate.mouseDown(valueLabel, { button: 0 }); | ||
expect(props.onClick, 'was called'); | ||
@@ -89,0 +89,0 @@ }); |
@@ -9,7 +9,10 @@ /* | ||
return { | ||
files: ['src/**/*.js', 'testHelpers/*.js'], | ||
files: ['src/**/*.js', { | ||
pattern: 'testHelpers/*.js', | ||
instrument: false | ||
}], | ||
tests: ['test/*-test.js' ], | ||
env: { | ||
type: 'node', | ||
runner: '/home/dave/.nvm/versions/node/v4.2.1/bin/node' | ||
runner: 'node' | ||
}, | ||
@@ -16,0 +19,0 @@ compilers: { |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
774883
83
11658
411
25
2
+ Addedreact-input-autosize@1.2.0(transitive)
- Removedreact-input-autosize@0.6.13(transitive)
Updatedclassnames@^2.2.4
Updatedreact-input-autosize@^1.1.0