Skip to content
This repository was archived by the owner on Jul 19, 2019. It is now read-only.

Commit e4c001a

Browse files
committed
first build/dist
(not sure exactly how rackt-cli works yet)
1 parent a5ea0b7 commit e4c001a

9 files changed

+1404
-0
lines changed

build/README.md

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
React Autocomplete
2+
==================
3+
4+
Accessible, extensible, Autocomplete for React.js.
5+
6+
Docs coming soon, for now just look at the `propTypes` and examples :)
7+

build/lib/Autocomplete.js

+346
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,346 @@
1+
'use strict';
2+
3+
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; };
4+
5+
var React = require('react');
6+
var scrollIntoView = require('dom-scroll-into-view');
7+
8+
var _debugStates = [];
9+
10+
var Autocomplete = React.createClass({
11+
displayName: 'Autocomplete',
12+
13+
propTypes: {
14+
initialValue: React.PropTypes.any,
15+
onChange: React.PropTypes.func,
16+
onSelect: React.PropTypes.func,
17+
shouldItemRender: React.PropTypes.func,
18+
renderItem: React.PropTypes.func.isRequired,
19+
menuStyle: React.PropTypes.object,
20+
inputProps: React.PropTypes.object
21+
},
22+
23+
getDefaultProps: function getDefaultProps() {
24+
return {
25+
inputProps: {},
26+
onChange: function onChange() {},
27+
onSelect: function onSelect(value, item) {},
28+
renderMenu: function renderMenu(items, value, style) {
29+
return React.createElement('div', { style: _extends({ style: style }, this.menuStyle), children: items });
30+
},
31+
shouldItemRender: function shouldItemRender() {
32+
return true;
33+
},
34+
menuStyle: {
35+
borderRadius: '3px',
36+
boxShadow: '0 2px 12px rgba(0, 0, 0, 0.1)',
37+
background: 'rgba(255, 255, 255, 0.9)',
38+
padding: '2px 0',
39+
fontSize: '90%',
40+
position: 'fixed',
41+
overflow: 'auto',
42+
maxHeight: '50%' }
43+
};
44+
},
45+
46+
// TODO: don't cheat, let it flow to the bottom
47+
getInitialState: function getInitialState() {
48+
return {
49+
value: this.props.initialValue || '',
50+
isOpen: false,
51+
highlightedIndex: null
52+
};
53+
},
54+
55+
componentWillMount: function componentWillMount() {
56+
this._ignoreBlur = false;
57+
this._performAutoCompleteOnUpdate = false;
58+
this._performAutoCompleteOnKeyUp = false;
59+
},
60+
61+
componentWillReceiveProps: function componentWillReceiveProps() {
62+
this._performAutoCompleteOnUpdate = true;
63+
},
64+
65+
componentDidUpdate: function componentDidUpdate(prevProps, prevState) {
66+
if (this.state.isOpen === true && prevState.isOpen === false) this.setMenuPositions();
67+
68+
if (this.state.isOpen && this._performAutoCompleteOnUpdate) {
69+
this._performAutoCompleteOnUpdate = false;
70+
this.maybeAutoCompleteText();
71+
}
72+
73+
this.maybeScrollItemIntoView();
74+
},
75+
76+
maybeScrollItemIntoView: function maybeScrollItemIntoView() {
77+
if (this.state.isOpen === true && this.state.highlightedIndex !== null) {
78+
var itemNode = React.findDOMNode(this.refs['item-' + this.state.highlightedIndex]);
79+
var menuNode = React.findDOMNode(this.refs.menu);
80+
scrollIntoView(itemNode, menuNode, { onlyScrollIfNeeded: true });
81+
}
82+
},
83+
84+
handleKeyDown: function handleKeyDown(event) {
85+
if (this.keyDownHandlers[event.key]) this.keyDownHandlers[event.key].call(this, event);else {
86+
this.setState({
87+
highlightedIndex: null,
88+
isOpen: true
89+
});
90+
}
91+
},
92+
93+
handleChange: function handleChange(event) {
94+
var _this = this;
95+
96+
console.log(event.target.value);
97+
this._performAutoCompleteOnKeyUp = true;
98+
this.setState({
99+
value: event.target.value
100+
}, function () {
101+
_this.props.onChange(event, _this.state.value);
102+
});
103+
},
104+
105+
handleKeyUp: function handleKeyUp() {
106+
if (this._performAutoCompleteOnKeyUp) {
107+
this._performAutoCompleteOnKeyUp = false;
108+
this.maybeAutoCompleteText();
109+
}
110+
},
111+
112+
keyDownHandlers: {
113+
ArrowDown: function ArrowDown() {
114+
event.preventDefault();
115+
var highlightedIndex = this.state.highlightedIndex;
116+
117+
var index = highlightedIndex === null || highlightedIndex === this.getFilteredItems().length - 1 ? 0 : highlightedIndex + 1;
118+
this._performAutoCompleteOnKeyUp = true;
119+
this.setState({
120+
highlightedIndex: index,
121+
isOpen: true
122+
});
123+
},
124+
125+
ArrowUp: function ArrowUp(event) {
126+
event.preventDefault();
127+
var highlightedIndex = this.state.highlightedIndex;
128+
129+
var index = highlightedIndex === 0 || highlightedIndex === null ? this.getFilteredItems().length - 1 : highlightedIndex - 1;
130+
this._performAutoCompleteOnKeyUp = true;
131+
this.setState({
132+
highlightedIndex: index,
133+
isOpen: true
134+
});
135+
},
136+
137+
Enter: function Enter(event) {
138+
var _this2 = this;
139+
140+
if (this.state.isOpen === false) {
141+
// already selected this, do nothing
142+
return;
143+
} else if (this.state.highlightedIndex == null) {
144+
// hit enter after focus but before typing anything so no autocomplete attempt yet
145+
this.setState({
146+
isOpen: false
147+
}, function () {
148+
React.findDOMNode(_this2.refs.input).select();
149+
});
150+
} else {
151+
var item = this.getFilteredItems()[this.state.highlightedIndex];
152+
this.setState({
153+
value: this.props.getItemValue(item),
154+
isOpen: false,
155+
highlightedIndex: null
156+
}, function () {
157+
//React.findDOMNode(this.refs.input).focus() // TODO: file issue
158+
React.findDOMNode(_this2.refs.input).setSelectionRange(_this2.state.value.length, _this2.state.value.length);
159+
_this2.props.onSelect(_this2.state.value, item);
160+
});
161+
}
162+
},
163+
164+
Escape: function Escape(event) {
165+
this.setState({
166+
highlightedIndex: null,
167+
isOpen: false
168+
});
169+
}
170+
},
171+
172+
getFilteredItems: function getFilteredItems() {
173+
var _this3 = this;
174+
175+
var items = this.props.items;
176+
177+
if (this.props.shouldItemRender) {
178+
items = items.filter(function (item) {
179+
return _this3.props.shouldItemRender(item, _this3.state.value);
180+
});
181+
}
182+
183+
if (this.props.sortItems) {
184+
items.sort(function (a, b) {
185+
return _this3.props.sortItems(a, b, _this3.state.value);
186+
});
187+
}
188+
189+
return items;
190+
},
191+
192+
maybeAutoCompleteText: function maybeAutoCompleteText() {
193+
var _this4 = this;
194+
195+
if (this.state.value === '') return;
196+
var highlightedIndex = this.state.highlightedIndex;
197+
198+
var items = this.getFilteredItems();
199+
if (items.length === 0) return;
200+
var matchedItem = highlightedIndex !== null ? items[highlightedIndex] : items[0];
201+
var itemValue = this.props.getItemValue(matchedItem);
202+
var itemValueDoesMatch = itemValue.toLowerCase().indexOf(this.state.value.toLowerCase()) === 0;
203+
if (itemValueDoesMatch) {
204+
var node = React.findDOMNode(this.refs.input);
205+
var setSelection = function setSelection() {
206+
node.value = itemValue;
207+
node.setSelectionRange(_this4.state.value.length, itemValue.length);
208+
};
209+
if (highlightedIndex === null) this.setState({ highlightedIndex: 0 }, setSelection);else setSelection();
210+
}
211+
},
212+
213+
setMenuPositions: function setMenuPositions() {
214+
var node = React.findDOMNode(this.refs.input);
215+
var rect = node.getBoundingClientRect();
216+
var computedStyle = getComputedStyle(node);
217+
var marginBottom = parseInt(computedStyle.marginBottom, 10);
218+
var marginLeft = parseInt(computedStyle.marginLeft, 10);
219+
var marginRight = parseInt(computedStyle.marginRight, 10);
220+
this.setState({
221+
menuTop: rect.bottom + marginBottom,
222+
menuLeft: rect.left + marginLeft,
223+
menuWidth: rect.width + marginLeft + marginRight
224+
});
225+
},
226+
227+
highlightItemFromMouse: function highlightItemFromMouse(index) {
228+
this.setState({ highlightedIndex: index });
229+
},
230+
231+
selectItemFromMouse: function selectItemFromMouse(item) {
232+
var _this5 = this;
233+
234+
this.setState({
235+
value: this.props.getItemValue(item),
236+
isOpen: false,
237+
highlightedIndex: null
238+
}, function () {
239+
_this5.props.onSelect(_this5.state.value, item);
240+
React.findDOMNode(_this5.refs.input).focus();
241+
_this5.setIgnoreBlur(false);
242+
});
243+
},
244+
245+
setIgnoreBlur: function setIgnoreBlur(ignore) {
246+
this._ignoreBlur = ignore;
247+
},
248+
249+
renderMenu: function renderMenu() {
250+
var _this6 = this;
251+
252+
var items = this.getFilteredItems().map(function (item, index) {
253+
var element = _this6.props.renderItem(item, _this6.state.highlightedIndex === index, { cursor: 'default' });
254+
return React.cloneElement(element, {
255+
onMouseDown: function onMouseDown() {
256+
return _this6.setIgnoreBlur(true);
257+
},
258+
onMouseEnter: function onMouseEnter() {
259+
return _this6.highlightItemFromMouse(index);
260+
},
261+
onClick: function onClick() {
262+
return _this6.selectItemFromMouse(item);
263+
},
264+
ref: 'item-' + index
265+
});
266+
});
267+
var style = {
268+
left: this.state.menuLeft,
269+
top: this.state.menuTop,
270+
minWidth: this.state.menuWidth
271+
};
272+
var menu = this.props.renderMenu(items, this.state.value, style);
273+
return React.cloneElement(menu, { ref: 'menu' });
274+
},
275+
276+
getActiveItemValue: function getActiveItemValue() {
277+
if (this.state.highlightedIndex === null) return '';else {
278+
var item = this.props.items[this.state.highlightedIndex];
279+
// items can match when we maybeAutoCompleteText, but then get replaced by the app
280+
// for the next render? I think? TODO: file an issue (alab -> enter -> type 'a' for
281+
// alabamaa and then an error would happen w/o this guard, pretty sure there's a
282+
// better way)
283+
return item ? this.props.getItemValue(item) : '';
284+
}
285+
},
286+
287+
handleInputBlur: function handleInputBlur() {
288+
if (this._ignoreBlur) return;
289+
this.setState({
290+
isOpen: false,
291+
highlightedIndex: null
292+
});
293+
},
294+
295+
handleInputFocus: function handleInputFocus() {
296+
if (this._ignoreBlur) return;
297+
this.setState({ isOpen: true });
298+
},
299+
300+
handleInputClick: function handleInputClick() {
301+
if (this.state.isOpen === false) this.setState({ isOpen: true });
302+
},
303+
304+
render: function render() {
305+
var _this7 = this;
306+
307+
if (this.props.debug) {
308+
// you don't like it, you love it
309+
_debugStates.push({
310+
id: _debugStates.length,
311+
state: this.state
312+
});
313+
}
314+
return React.createElement(
315+
'div',
316+
{ style: { display: 'inline-block' } },
317+
React.createElement('input', _extends({}, this.props.inputProps, {
318+
role: 'combobox',
319+
'aria-autocomplete': 'both',
320+
'aria-label': this.getActiveItemValue(),
321+
ref: 'input',
322+
onFocus: this.handleInputFocus,
323+
onBlur: this.handleInputBlur,
324+
onChange: function (event) {
325+
return _this7.handleChange(event);
326+
},
327+
onKeyDown: function (event) {
328+
return _this7.handleKeyDown(event);
329+
},
330+
onKeyUp: function (event) {
331+
return _this7.handleKeyUp(event);
332+
},
333+
onClick: this.handleInputClick,
334+
value: this.state.value
335+
})),
336+
this.state.isOpen && this.renderMenu(),
337+
this.props.debug && React.createElement(
338+
'pre',
339+
{ style: { marginLeft: 300 } },
340+
JSON.stringify(_debugStates.slice(_debugStates.length - 5, _debugStates.length), null, 2)
341+
)
342+
);
343+
}
344+
});
345+
346+
module.exports = Autocomplete;
+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
'use strict';
2+
3+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
4+
5+
var _reactAddons = require('react/addons');
6+
7+
var _reactAddons2 = _interopRequireDefault(_reactAddons);
8+
9+
var _Autocomplete = require('../Autocomplete');
10+
11+
var _Autocomplete2 = _interopRequireDefault(_Autocomplete);
12+
13+
var _assert = require('assert');
14+
15+
/* eslint func-names:0 */
16+
var TestUtils = _reactAddons2['default'].addons.TestUtils;
17+
describe('react-autocomplete', function () {
18+
it('should have tests', function () {
19+
(0, _assert.ok)(false);
20+
});
21+
});

build/lib/index.js

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
'use strict';
2+
3+
module.exports = require('./Autocomplete');

0 commit comments

Comments
 (0)