react-autocomplete
Advanced tools
Comparing version 0.1.0 to 0.1.1
@@ -1,13 +0,8 @@ | ||
'use strict'; | ||
const React = require('react') | ||
const scrollIntoView = require('dom-scroll-into-view') | ||
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; }; | ||
let _debugStates = [] | ||
var React = require('react'); | ||
var scrollIntoView = require('dom-scroll-into-view'); | ||
let Autocomplete = React.createClass({ | ||
var _debugStates = []; | ||
var Autocomplete = React.createClass({ | ||
displayName: 'Autocomplete', | ||
propTypes: { | ||
@@ -23,13 +18,11 @@ initialValue: React.PropTypes.any, | ||
getDefaultProps: function getDefaultProps() { | ||
getDefaultProps () { | ||
return { | ||
inputProps: {}, | ||
onChange: function onChange() {}, | ||
onSelect: function onSelect(value, item) {}, | ||
renderMenu: function renderMenu(items, value, style) { | ||
return React.createElement('div', { style: _extends({ style: style }, this.menuStyle), children: items }); | ||
onChange () {}, | ||
onSelect (value, item) {}, | ||
renderMenu (items, value, style) { | ||
return <div style={{style, ...this.menuStyle}} children={items}/> | ||
}, | ||
shouldItemRender: function shouldItemRender() { | ||
return true; | ||
}, | ||
shouldItemRender () { return true }, | ||
menuStyle: { | ||
@@ -43,69 +36,69 @@ borderRadius: '3px', | ||
overflow: 'auto', | ||
maxHeight: '50%' } | ||
}; | ||
maxHeight: '50%', // TODO: don't cheat, let it flow to the bottom | ||
} | ||
} | ||
}, | ||
// TODO: don't cheat, let it flow to the bottom | ||
getInitialState: function getInitialState() { | ||
getInitialState () { | ||
return { | ||
value: this.props.initialValue || '', | ||
isOpen: false, | ||
highlightedIndex: null | ||
}; | ||
highlightedIndex: null, | ||
} | ||
}, | ||
componentWillMount: function componentWillMount() { | ||
this._ignoreBlur = false; | ||
this._performAutoCompleteOnUpdate = false; | ||
this._performAutoCompleteOnKeyUp = false; | ||
componentWillMount () { | ||
this._ignoreBlur = false | ||
this._performAutoCompleteOnUpdate = false | ||
this._performAutoCompleteOnKeyUp = false | ||
}, | ||
componentWillReceiveProps: function componentWillReceiveProps() { | ||
this._performAutoCompleteOnUpdate = true; | ||
componentWillReceiveProps () { | ||
this._performAutoCompleteOnUpdate = true | ||
}, | ||
componentDidUpdate: function componentDidUpdate(prevProps, prevState) { | ||
if (this.state.isOpen === true && prevState.isOpen === false) this.setMenuPositions(); | ||
componentDidUpdate (prevProps, prevState) { | ||
if (this.state.isOpen === true && prevState.isOpen === false) | ||
this.setMenuPositions() | ||
if (this.state.isOpen && this._performAutoCompleteOnUpdate) { | ||
this._performAutoCompleteOnUpdate = false; | ||
this.maybeAutoCompleteText(); | ||
this._performAutoCompleteOnUpdate = false | ||
this.maybeAutoCompleteText() | ||
} | ||
this.maybeScrollItemIntoView(); | ||
this.maybeScrollItemIntoView() | ||
}, | ||
maybeScrollItemIntoView: function maybeScrollItemIntoView() { | ||
maybeScrollItemIntoView () { | ||
if (this.state.isOpen === true && this.state.highlightedIndex !== null) { | ||
var itemNode = React.findDOMNode(this.refs['item-' + this.state.highlightedIndex]); | ||
var menuNode = React.findDOMNode(this.refs.menu); | ||
scrollIntoView(itemNode, menuNode, { onlyScrollIfNeeded: true }); | ||
var itemNode = React.findDOMNode(this.refs[`item-${this.state.highlightedIndex}`]) | ||
var menuNode = React.findDOMNode(this.refs.menu) | ||
scrollIntoView(itemNode, menuNode, { onlyScrollIfNeeded: true }) | ||
} | ||
}, | ||
handleKeyDown: function handleKeyDown(event) { | ||
if (this.keyDownHandlers[event.key]) this.keyDownHandlers[event.key].call(this, event);else { | ||
handleKeyDown (event) { | ||
if (this.keyDownHandlers[event.key]) | ||
this.keyDownHandlers[event.key].call(this, event) | ||
else { | ||
this.setState({ | ||
highlightedIndex: null, | ||
isOpen: true | ||
}); | ||
}) | ||
} | ||
}, | ||
handleChange: function handleChange(event) { | ||
var _this = this; | ||
console.log(event.target.value); | ||
this._performAutoCompleteOnKeyUp = true; | ||
handleChange (event) { | ||
this._performAutoCompleteOnKeyUp = true | ||
this.setState({ | ||
value: event.target.value | ||
}, function () { | ||
_this.props.onChange(event, _this.state.value); | ||
}); | ||
value: event.target.value, | ||
}, () => { | ||
this.props.onChange(event, this.state.value) | ||
}) | ||
}, | ||
handleKeyUp: function handleKeyUp() { | ||
handleKeyUp () { | ||
if (this._performAutoCompleteOnKeyUp) { | ||
this._performAutoCompleteOnKeyUp = false; | ||
this.maybeAutoCompleteText(); | ||
this._performAutoCompleteOnKeyUp = false | ||
this.maybeAutoCompleteText() | ||
} | ||
@@ -115,41 +108,45 @@ }, | ||
keyDownHandlers: { | ||
ArrowDown: function ArrowDown() { | ||
event.preventDefault(); | ||
var highlightedIndex = this.state.highlightedIndex; | ||
var index = highlightedIndex === null || highlightedIndex === this.getFilteredItems().length - 1 ? 0 : highlightedIndex + 1; | ||
this._performAutoCompleteOnKeyUp = true; | ||
ArrowDown () { | ||
event.preventDefault() | ||
var { highlightedIndex } = this.state | ||
var index = ( | ||
highlightedIndex === null || | ||
highlightedIndex === this.getFilteredItems().length - 1 | ||
) ? 0 : highlightedIndex + 1 | ||
this._performAutoCompleteOnKeyUp = true | ||
this.setState({ | ||
highlightedIndex: index, | ||
isOpen: true | ||
}); | ||
isOpen: true, | ||
}) | ||
}, | ||
ArrowUp: function ArrowUp(event) { | ||
event.preventDefault(); | ||
var highlightedIndex = this.state.highlightedIndex; | ||
var index = highlightedIndex === 0 || highlightedIndex === null ? this.getFilteredItems().length - 1 : highlightedIndex - 1; | ||
this._performAutoCompleteOnKeyUp = true; | ||
ArrowUp (event) { | ||
event.preventDefault() | ||
var { highlightedIndex } = this.state | ||
var index = ( | ||
highlightedIndex === 0 || | ||
highlightedIndex === null | ||
) ? this.getFilteredItems().length - 1 : highlightedIndex - 1 | ||
this._performAutoCompleteOnKeyUp = true | ||
this.setState({ | ||
highlightedIndex: index, | ||
isOpen: true | ||
}); | ||
isOpen: true, | ||
}) | ||
}, | ||
Enter: function Enter(event) { | ||
var _this2 = this; | ||
Enter (event) { | ||
if (this.state.isOpen === false) { | ||
// already selected this, do nothing | ||
return; | ||
} else if (this.state.highlightedIndex == null) { | ||
return | ||
} | ||
else if (this.state.highlightedIndex == null) { | ||
// hit enter after focus but before typing anything so no autocomplete attempt yet | ||
this.setState({ | ||
isOpen: false | ||
}, function () { | ||
React.findDOMNode(_this2.refs.input).select(); | ||
}); | ||
} else { | ||
var item = this.getFilteredItems()[this.state.highlightedIndex]; | ||
}, () => { | ||
React.findDOMNode(this.refs.input).select() | ||
}) | ||
} | ||
else { | ||
var item = this.getFilteredItems()[this.state.highlightedIndex] | ||
this.setState({ | ||
@@ -159,66 +156,72 @@ value: this.props.getItemValue(item), | ||
highlightedIndex: null | ||
}, function () { | ||
}, () => { | ||
//React.findDOMNode(this.refs.input).focus() // TODO: file issue | ||
React.findDOMNode(_this2.refs.input).setSelectionRange(_this2.state.value.length, _this2.state.value.length); | ||
_this2.props.onSelect(_this2.state.value, item); | ||
}); | ||
React.findDOMNode(this.refs.input).setSelectionRange( | ||
this.state.value.length, | ||
this.state.value.length | ||
) | ||
this.props.onSelect(this.state.value, item) | ||
}) | ||
} | ||
}, | ||
Escape: function Escape(event) { | ||
Escape (event) { | ||
this.setState({ | ||
highlightedIndex: null, | ||
isOpen: false | ||
}); | ||
}) | ||
} | ||
}, | ||
getFilteredItems: function getFilteredItems() { | ||
var _this3 = this; | ||
getFilteredItems () { | ||
let items = this.props.items | ||
var items = this.props.items; | ||
if (this.props.shouldItemRender) { | ||
items = items.filter(function (item) { | ||
return _this3.props.shouldItemRender(item, _this3.state.value); | ||
}); | ||
items = items.filter((item) => ( | ||
this.props.shouldItemRender(item, this.state.value) | ||
)) | ||
} | ||
if (this.props.sortItems) { | ||
items.sort(function (a, b) { | ||
return _this3.props.sortItems(a, b, _this3.state.value); | ||
}); | ||
items.sort((a, b) => ( | ||
this.props.sortItems(a, b, this.state.value) | ||
)) | ||
} | ||
return items; | ||
return items | ||
}, | ||
maybeAutoCompleteText: function maybeAutoCompleteText() { | ||
var _this4 = this; | ||
if (this.state.value === '') return; | ||
var highlightedIndex = this.state.highlightedIndex; | ||
var items = this.getFilteredItems(); | ||
if (items.length === 0) return; | ||
var matchedItem = highlightedIndex !== null ? items[highlightedIndex] : items[0]; | ||
var itemValue = this.props.getItemValue(matchedItem); | ||
var itemValueDoesMatch = itemValue.toLowerCase().indexOf(this.state.value.toLowerCase()) === 0; | ||
maybeAutoCompleteText () { | ||
if (this.state.value === '') | ||
return | ||
var { highlightedIndex } = this.state | ||
var items = this.getFilteredItems() | ||
if (items.length === 0) | ||
return | ||
var matchedItem = highlightedIndex !== null ? | ||
items[highlightedIndex] : items[0] | ||
var itemValue = this.props.getItemValue(matchedItem) | ||
var itemValueDoesMatch = (itemValue.toLowerCase().indexOf( | ||
this.state.value.toLowerCase() | ||
) === 0) | ||
if (itemValueDoesMatch) { | ||
var node = React.findDOMNode(this.refs.input); | ||
var setSelection = function setSelection() { | ||
node.value = itemValue; | ||
node.setSelectionRange(_this4.state.value.length, itemValue.length); | ||
}; | ||
if (highlightedIndex === null) this.setState({ highlightedIndex: 0 }, setSelection);else setSelection(); | ||
var node = React.findDOMNode(this.refs.input) | ||
var setSelection = () => { | ||
node.value = itemValue | ||
node.setSelectionRange(this.state.value.length, itemValue.length) | ||
} | ||
if (highlightedIndex === null) | ||
this.setState({ highlightedIndex: 0 }, setSelection) | ||
else | ||
setSelection() | ||
} | ||
}, | ||
setMenuPositions: function setMenuPositions() { | ||
var node = React.findDOMNode(this.refs.input); | ||
var rect = node.getBoundingClientRect(); | ||
var computedStyle = getComputedStyle(node); | ||
var marginBottom = parseInt(computedStyle.marginBottom, 10); | ||
var marginLeft = parseInt(computedStyle.marginLeft, 10); | ||
var marginRight = parseInt(computedStyle.marginRight, 10); | ||
setMenuPositions () { | ||
var node = React.findDOMNode(this.refs.input) | ||
var rect = node.getBoundingClientRect() | ||
var computedStyle = getComputedStyle(node) | ||
var marginBottom = parseInt(computedStyle.marginBottom, 10) | ||
var marginLeft = parseInt(computedStyle.marginLeft, 10) | ||
var marginRight = parseInt(computedStyle.marginRight, 10) | ||
this.setState({ | ||
@@ -228,12 +231,10 @@ menuTop: rect.bottom + marginBottom, | ||
menuWidth: rect.width + marginLeft + marginRight | ||
}); | ||
}) | ||
}, | ||
highlightItemFromMouse: function highlightItemFromMouse(index) { | ||
this.setState({ highlightedIndex: index }); | ||
highlightItemFromMouse (index) { | ||
this.setState({ highlightedIndex: index }) | ||
}, | ||
selectItemFromMouse: function selectItemFromMouse(item) { | ||
var _this5 = this; | ||
selectItemFromMouse (item) { | ||
this.setState({ | ||
@@ -243,43 +244,41 @@ value: this.props.getItemValue(item), | ||
highlightedIndex: null | ||
}, function () { | ||
_this5.props.onSelect(_this5.state.value, item); | ||
React.findDOMNode(_this5.refs.input).focus(); | ||
_this5.setIgnoreBlur(false); | ||
}); | ||
}, () => { | ||
this.props.onSelect(this.state.value, item) | ||
React.findDOMNode(this.refs.input).focus() | ||
this.setIgnoreBlur(false) | ||
}) | ||
}, | ||
setIgnoreBlur: function setIgnoreBlur(ignore) { | ||
this._ignoreBlur = ignore; | ||
setIgnoreBlur (ignore) { | ||
this._ignoreBlur = ignore | ||
}, | ||
renderMenu: function renderMenu() { | ||
var _this6 = this; | ||
var items = this.getFilteredItems().map(function (item, index) { | ||
var element = _this6.props.renderItem(item, _this6.state.highlightedIndex === index, { cursor: 'default' }); | ||
renderMenu () { | ||
var items = this.getFilteredItems().map((item, index) => { | ||
var element = this.props.renderItem( | ||
item, | ||
this.state.highlightedIndex === index, | ||
{cursor: 'default'} | ||
) | ||
return React.cloneElement(element, { | ||
onMouseDown: function onMouseDown() { | ||
return _this6.setIgnoreBlur(true); | ||
}, | ||
onMouseEnter: function onMouseEnter() { | ||
return _this6.highlightItemFromMouse(index); | ||
}, | ||
onClick: function onClick() { | ||
return _this6.selectItemFromMouse(item); | ||
}, | ||
ref: 'item-' + index | ||
}); | ||
}); | ||
onMouseDown: () => this.setIgnoreBlur(true), | ||
onMouseEnter: () => this.highlightItemFromMouse(index), | ||
onClick: () => this.selectItemFromMouse(item), | ||
ref: `item-${index}`, | ||
}) | ||
}) | ||
var style = { | ||
left: this.state.menuLeft, | ||
top: this.state.menuTop, | ||
minWidth: this.state.menuWidth | ||
}; | ||
var menu = this.props.renderMenu(items, this.state.value, style); | ||
return React.cloneElement(menu, { ref: 'menu' }); | ||
minWidth: this.state.menuWidth, | ||
} | ||
var menu = this.props.renderMenu(items, this.state.value, style) | ||
return React.cloneElement(menu, { ref: 'menu' }) | ||
}, | ||
getActiveItemValue: function getActiveItemValue() { | ||
if (this.state.highlightedIndex === null) return '';else { | ||
var item = this.props.items[this.state.highlightedIndex]; | ||
getActiveItemValue () { | ||
if (this.state.highlightedIndex === null) | ||
return '' | ||
else { | ||
var item = this.props.items[this.state.highlightedIndex] | ||
// items can match when we maybeAutoCompleteText, but then get replaced by the app | ||
@@ -289,65 +288,61 @@ // for the next render? I think? TODO: file an issue (alab -> enter -> type 'a' for | ||
// better way) | ||
return item ? this.props.getItemValue(item) : ''; | ||
return item ? this.props.getItemValue(item) : '' | ||
} | ||
}, | ||
handleInputBlur: function handleInputBlur() { | ||
if (this._ignoreBlur) return; | ||
handleInputBlur () { | ||
if (this._ignoreBlur) | ||
return | ||
this.setState({ | ||
isOpen: false, | ||
highlightedIndex: null | ||
}); | ||
}) | ||
}, | ||
handleInputFocus: function handleInputFocus() { | ||
if (this._ignoreBlur) return; | ||
this.setState({ isOpen: true }); | ||
handleInputFocus () { | ||
if (this._ignoreBlur) | ||
return | ||
this.setState({ isOpen: true }) | ||
}, | ||
handleInputClick: function handleInputClick() { | ||
if (this.state.isOpen === false) this.setState({ isOpen: true }); | ||
handleInputClick () { | ||
if (this.state.isOpen === false) | ||
this.setState({ isOpen: true }) | ||
}, | ||
render: function render() { | ||
var _this7 = this; | ||
if (this.props.debug) { | ||
// you don't like it, you love it | ||
render () { | ||
if (this.props.debug) { // you don't like it, you love it | ||
_debugStates.push({ | ||
id: _debugStates.length, | ||
state: this.state | ||
}); | ||
}) | ||
} | ||
return React.createElement( | ||
'div', | ||
{ style: { display: 'inline-block' } }, | ||
React.createElement('input', _extends({}, this.props.inputProps, { | ||
role: 'combobox', | ||
'aria-autocomplete': 'both', | ||
'aria-label': this.getActiveItemValue(), | ||
ref: 'input', | ||
onFocus: this.handleInputFocus, | ||
onBlur: this.handleInputBlur, | ||
onChange: function (event) { | ||
return _this7.handleChange(event); | ||
}, | ||
onKeyDown: function (event) { | ||
return _this7.handleKeyDown(event); | ||
}, | ||
onKeyUp: function (event) { | ||
return _this7.handleKeyUp(event); | ||
}, | ||
onClick: this.handleInputClick, | ||
value: this.state.value | ||
})), | ||
this.state.isOpen && this.renderMenu(), | ||
this.props.debug && React.createElement( | ||
'pre', | ||
{ style: { marginLeft: 300 } }, | ||
JSON.stringify(_debugStates.slice(_debugStates.length - 5, _debugStates.length), null, 2) | ||
) | ||
); | ||
return ( | ||
<div style={{display: 'inline-block'}}> | ||
<input | ||
{...this.props.inputProps} | ||
role="combobox" | ||
aria-autocomplete="both" | ||
aria-label={this.getActiveItemValue()} | ||
ref="input" | ||
onFocus={this.handleInputFocus} | ||
onBlur={this.handleInputBlur} | ||
onChange={(event) => this.handleChange(event)} | ||
onKeyDown={(event) => this.handleKeyDown(event)} | ||
onKeyUp={(event) => this.handleKeyUp(event)} | ||
onClick={this.handleInputClick} | ||
value={this.state.value} | ||
/> | ||
{this.state.isOpen && this.renderMenu()} | ||
{this.props.debug && ( | ||
<pre style={{marginLeft: 300}}> | ||
{JSON.stringify(_debugStates.slice(_debugStates.length - 5, _debugStates.length), null, 2)} | ||
</pre> | ||
)} | ||
</div> | ||
) | ||
} | ||
}); | ||
}) | ||
module.exports = Autocomplete; | ||
module.exports = Autocomplete | ||
@@ -1,3 +0,1 @@ | ||
'use strict'; | ||
module.exports = require('./Autocomplete'); | ||
module.exports = require('./Autocomplete') |
{ | ||
"name": "react-autocomplete", | ||
"version": "0.1.0", | ||
"version": "0.1.1", | ||
"description": "Accessible, extensible, Autocomplete for React.js", | ||
@@ -15,2 +15,6 @@ "main": "./lib/index.js", | ||
}, | ||
"scripts": { | ||
"test": "echo 'lol'", | ||
"start": "rackt server" | ||
}, | ||
"authors": [ | ||
@@ -20,2 +24,6 @@ "Ryan Florence <rpflorence@gmail.com>" | ||
"license": "MIT", | ||
"devDependencies": { | ||
"rackt-cli": "^0.4.0", | ||
"react": "^0.13.3" | ||
}, | ||
"tags": [ | ||
@@ -31,2 +39,2 @@ "react", | ||
} | ||
} | ||
} |
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
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
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
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
190024
15
1530
2
2