diff --git a/.eslintrc b/.eslintrc
index 50fa67e3..136e551f 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -162,6 +162,7 @@
"location": "start"
}],
"no-with": [2],
+ "object-shorthand": [2],
"one-var": [0],
"operator-assignment": [0, "always"],
"operator-linebreak": [2, "before"],
@@ -200,7 +201,6 @@
"yoda": [2, "never", {
"exceptRange": true
}],
- "babel/object-shorthand": [2],
"react/display-name": 0,
"react/jsx-boolean-value": 1,
"react/jsx-closing-bracket-location": 0,
diff --git a/components/autocomplete/Autocomplete.js b/components/autocomplete/Autocomplete.js
index 8ed3b68d..67439bd6 100644
--- a/components/autocomplete/Autocomplete.js
+++ b/components/autocomplete/Autocomplete.js
@@ -21,6 +21,7 @@ const factory = (Chip, Input) => {
direction: PropTypes.oneOf(['auto', 'up', 'down']),
disabled: PropTypes.bool,
error: PropTypes.string,
+ keepFocusOnChange: PropTypes.bool,
label: PropTypes.string,
multiple: PropTypes.bool,
onBlur: PropTypes.func,
@@ -28,6 +29,7 @@ const factory = (Chip, Input) => {
onQueryChange: PropTypes.func,
onFocus: PropTypes.func,
selectedPosition: PropTypes.oneOf(['above', 'below', 'none']),
+ showSelectedWhenNotInSource: PropTypes.bool,
showSuggestionsWhenValueIsSet: PropTypes.bool,
source: PropTypes.any,
suggestionMatch: PropTypes.oneOf(['start', 'anywhere', 'word']),
@@ -36,7 +38,6 @@ const factory = (Chip, Input) => {
autocomplete: PropTypes.string,
focus: PropTypes.string,
input: PropTypes.string,
- label: PropTypes.string,
suggestion: PropTypes.string,
suggestions: PropTypes.string,
up: PropTypes.string,
@@ -50,9 +51,11 @@ const factory = (Chip, Input) => {
allowCreate: false,
className: '',
direction: 'auto',
- selectedPosition: 'above',
+ keepFocusOnChange: false,
multiple: true,
+ selectedPosition: 'above',
showSuggestionsWhenValueIsSet: false,
+ showSelectedWhenNotInSource: false,
source: {},
suggestionMatch: 'start'
};
@@ -61,15 +64,31 @@ const factory = (Chip, Input) => {
direction: this.props.direction,
focus: false,
showAllSuggestions: this.props.showSuggestionsWhenValueIsSet,
- query: this.query(this.props.value)
+ query: this.query(this.props.value),
+ valueIsObject: false
};
+ componentDidMount () {
+ this.setState({
+ // TODO: Move to method
+ valueIsObject: !Array.isArray(this.props.value) && typeof this.props.value === 'object'
+ });
+ }
+
componentWillReceiveProps (nextProps) {
if (!this.props.multiple) {
this.setState({
query: this.query(nextProps.value)
});
}
+ // TODO: Better comparison?
+ if (nextProps.multiple && this.props.value !== nextProps.value) {
+ console.log('value has changed fo real', nextProps.value);
+ this.setState({
+ // TODO: Move to method
+ valueIsObject: !Array.isArray(nextProps.value) && typeof nextProps.value === 'object'
+ });
+ }
}
shouldComponentUpdate (nextProps, nextState) {
@@ -82,14 +101,25 @@ const factory = (Chip, Input) => {
return true;
}
- handleChange = (keys, event) => {
- const key = this.props.multiple ? keys : keys[0];
- const query = this.query(key);
- if (this.props.onChange) this.props.onChange(key, event);
- this.setState(
- {focus: false, query, showAllSuggestions: this.props.showSuggestionsWhenValueIsSet},
- () => { ReactDOM.findDOMNode(this).querySelector('input').blur(); }
- );
+ handleChange = (values, event) => {
+ console.log('handle change', values);
+ const value = this.props.multiple ? values : values[0];
+ console.log('new keys here in handle change', value);
+ const { showSuggestionsWhenValueIsSet: showAllSuggestions } = this.props;
+ const query = this.query(value);
+ // TODO: Pass back key/value if object originally supplied for value
+ if (this.props.onChange) this.props.onChange(value, event);
+ if (this.props.keepFocusOnChange) {
+ this.setState({ query, showAllSuggestions });
+ } else {
+ this.setState({ focus: false, query, showAllSuggestions }, () => {
+ ReactDOM.findDOMNode(this).querySelector('input').blur();
+ });
+ }
+ };
+
+ handleMouseDown = () => {
+ this.selectOrCreateActiveItem();
};
handleQueryBlur = (event) => {
@@ -99,11 +129,11 @@ const factory = (Chip, Input) => {
handleQueryChange = (value) => {
if (this.props.onQueryChange) this.props.onQueryChange(value);
- this.setState({query: value, showAllSuggestions: false});
+ this.setState({query: value, showAllSuggestions: false, active: null});
};
handleQueryFocus = () => {
- this.refs.suggestions.scrollTop = 0;
+ this.suggestionsNode.scrollTop = 0;
this.setState({active: '', focus: true});
if (this.props.onFocus) this.props.onFocus();
};
@@ -120,14 +150,7 @@ const factory = (Chip, Input) => {
}
if (event.which === 13) {
- let target = this.state.active;
- if (!target) {
- target = this.props.allowCreate
- ? this.state.query
- : [...this.suggestions().keys()][0];
- this.setState({active: target});
- }
- this.select(event, target);
+ this.selectOrCreateActiveItem();
}
};
@@ -149,7 +172,7 @@ const factory = (Chip, Input) => {
calculateDirection () {
if (this.props.direction === 'auto') {
- const client = ReactDOM.findDOMNode(this.refs.input).getBoundingClientRect();
+ const client = ReactDOM.findDOMNode(this.inputNode).getBoundingClientRect();
const screen_height = window.innerHeight || document.documentElement.offsetHeight;
const up = client.top > ((screen_height / 2) + client.height);
return up ? 'up' : 'down';
@@ -165,7 +188,18 @@ const factory = (Chip, Input) => {
query_value = source_value ? source_value : key;
}
return query_value;
- }
+ }
+
+ selectOrCreateActiveItem () {
+ let target = this.state.active;
+ if (!target) {
+ target = this.props.allowCreate
+ ? this.state.query
+ : [...this.suggestions().keys()][0];
+ this.setState({active: target});
+ }
+ this.select(event, target);
+ }
suggestions () {
let suggest = new Map();
@@ -174,15 +208,17 @@ const factory = (Chip, Input) => {
const values = this.values();
const source = this.source();
+ console.log('in suggestions', values);
// Suggest any non-set value which matches the query
if (this.props.multiple) {
for (const [key, value] of source) {
- if (!values.has(key) && this.matches(value.toLowerCase().trim(), query)) {
+ //if ((Array.isArray(values) && !values.has(key)) && this.matches(value.toLowerCase().trim(), query)) {
+ if ((!values.has(key)) && this.matches(value.toLowerCase().trim(), query)) {
suggest.set(key, value);
}
}
- // When multiple is false, suggest any value which matches the query if showAllSuggestions is false
+ // When multipleArray is false, suggest any value which matches the query if showAllSuggestions is false
} else if (query && !this.state.showAllSuggestions) {
for (const [key, value] of source) {
if (this.matches(value.toLowerCase().trim(), query)) {
@@ -190,7 +226,7 @@ const factory = (Chip, Input) => {
}
}
- // When multiple is false, suggest all values when showAllSuggestions is true
+ // When multipleArray is false, suggest all values when showAllSuggestions is true
} else {
suggest = source;
}
@@ -223,43 +259,117 @@ const factory = (Chip, Input) => {
}
values () {
- const valueMap = new Map();
const vals = this.props.multiple ? this.props.value : [this.props.value];
+
+ if (this.props.showSelectedWhenNotInSource && typeof vals === 'object') {
+ return new Map(Object.entries(vals));
+ }
+
+ const valueMap = new Map();
for (const [k, v] of this.source()) {
- if (vals.indexOf(k) !== -1) valueMap.set(k, v);
+ if ((Array.isArray(vals) && vals.indexOf(k) !== -1) || (k in vals)) {
+ valueMap.set(k, v);
+ }
}
return valueMap;
}
select = (event, target) => {
events.pauseEvent(event);
- const values = this.values(this.props.value);
+ let values = this.values(this.props.value);
+ const source = this.source();
const newValue = target === void 0 ? event.target.id : target;
+ console.log('selected', target, event.target);
+ if (this.state.valueIsObject) {
+ console.log('current values', values);
+ console.log('new value', newValue);
+
+ const sourceObj = Array.from(source).reduce((obj, [k, value]) => {
+ console.log('reducer', 'key', k, 'value', value);
+ if (k === newValue) {
+ obj[k] = value; // Be careful! ES6 Maps may have non-String keys.
+ }
+ return obj;
+ }, {});
+
+ values = Array.from(values).reduce((obj, [ke, value]) => {
+ console.log('reducer', 'key', ke, 'value', value);
+ obj[ke] = value; // Be careful! ES6 Maps may have non-String keys.
+ return obj;
+ }, {});
+
+ console.log('new obj', sourceObj);
+
+ return this.handleChange(Object.assign(values, sourceObj), event);
+ }
+
this.handleChange([newValue, ...values.keys()], event);
};
unselect (key, event) {
if (!this.props.disabled) {
const values = this.values(this.props.value);
+
+ console.log('unselected vals', values, 'key', key);
+ /*if (typeof values === 'object') {
+ delete values[key];
+
+ return this.handleChange(Object.keys(values), event);
+ }*/
+
values.delete(key);
+
+ console.log('new keys', values.keys());
+
+ if (this.state.valueIsObject) {
+ const valuesObj = Array.from(values).reduce((obj, [k, value]) => {
+ obj[k] = value; // Be careful! ES6 Maps may have non-String keys.
+ return obj;
+ }, {});
+
+ return this.handleChange(valuesObj, event);
+ }
this.handleChange([...values.keys()], event);
}
}
renderSelected () {
if (this.props.multiple) {
- const selectedItems = [...this.values()].map(([key, value]) => {
- return (
-