Merge branch 'show-chips-if-not-in-source' into dev
commit
d8a73eef88
|
@ -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,
|
||||
|
|
|
@ -27,7 +27,9 @@ const factory = (Chip, Input) => {
|
|||
onBlur: PropTypes.func,
|
||||
onChange: PropTypes.func,
|
||||
onFocus: PropTypes.func,
|
||||
selectedPosition: PropTypes.oneOf(['above', 'below']),
|
||||
onQueryChange: PropTypes.func,
|
||||
selectedPosition: PropTypes.oneOf(['above', 'below', 'none']),
|
||||
showSelectedWhenNotInSource: PropTypes.bool,
|
||||
showSuggestionsWhenValueIsSet: PropTypes.bool,
|
||||
source: PropTypes.any,
|
||||
suggestionMatch: PropTypes.oneOf(['start', 'anywhere', 'word']),
|
||||
|
@ -53,6 +55,7 @@ const factory = (Chip, Input) => {
|
|||
multiple: true,
|
||||
selectedPosition: 'above',
|
||||
showSuggestionsWhenValueIsSet: false,
|
||||
showSelectedWhenNotInSource: false,
|
||||
source: {},
|
||||
suggestionMatch: 'start'
|
||||
};
|
||||
|
@ -61,15 +64,24 @@ 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),
|
||||
isValueAnObject: false
|
||||
};
|
||||
|
||||
componentDidMount () {
|
||||
this.setIsValueAnObject();
|
||||
}
|
||||
|
||||
componentWillReceiveProps (nextProps) {
|
||||
if (!this.props.multiple) {
|
||||
this.setState({
|
||||
query: this.query(nextProps.value)
|
||||
});
|
||||
}
|
||||
|
||||
if (nextProps.multiple && this.props.value !== nextProps.value) {
|
||||
this.setIsValueAnObject();
|
||||
}
|
||||
}
|
||||
|
||||
shouldComponentUpdate (nextProps, nextState) {
|
||||
|
@ -82,11 +94,11 @@ const factory = (Chip, Input) => {
|
|||
return true;
|
||||
}
|
||||
|
||||
handleChange = (keys, event) => {
|
||||
const key = this.props.multiple ? keys : keys[0];
|
||||
handleChange = (values, event) => {
|
||||
const value = this.props.multiple ? values : values[0];
|
||||
const { showSuggestionsWhenValueIsSet: showAllSuggestions } = this.props;
|
||||
const query = this.query(key);
|
||||
if (this.props.onChange) this.props.onChange(key, event);
|
||||
const query = this.query(value);
|
||||
if (this.props.onChange) this.props.onChange(value, event);
|
||||
if (this.props.keepFocusOnChange) {
|
||||
this.setState({ query, showAllSuggestions });
|
||||
} else {
|
||||
|
@ -96,9 +108,9 @@ const factory = (Chip, Input) => {
|
|||
}
|
||||
};
|
||||
|
||||
handleMouseDown = event => {
|
||||
handleMouseDown = () => {
|
||||
this.selectOrCreateActiveItem();
|
||||
}
|
||||
};
|
||||
|
||||
handleQueryBlur = (event) => {
|
||||
if (this.state.focus) this.setState({focus: false});
|
||||
|
@ -106,6 +118,7 @@ const factory = (Chip, Input) => {
|
|||
};
|
||||
|
||||
handleQueryChange = (value) => {
|
||||
if (this.props.onQueryChange) this.props.onQueryChange(value);
|
||||
this.setState({query: value, showAllSuggestions: false, active: null});
|
||||
};
|
||||
|
||||
|
@ -234,10 +247,17 @@ const factory = (Chip, Input) => {
|
|||
}
|
||||
|
||||
values () {
|
||||
const valueMap = new Map();
|
||||
const vals = this.props.multiple ? this.props.value : [this.props.value];
|
||||
|
||||
if (this.props.showSelectedWhenNotInSource && this.state.isValueAnObject) {
|
||||
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;
|
||||
}
|
||||
|
@ -245,18 +265,50 @@ const factory = (Chip, Input) => {
|
|||
select = (event, target) => {
|
||||
events.pauseEvent(event);
|
||||
const values = this.values(this.props.value);
|
||||
const source = this.source();
|
||||
const newValue = target === void 0 ? event.target.id : target;
|
||||
|
||||
if (this.state.isValueAnObject) {
|
||||
const newItem = Array.from(source).reduce((obj, [k, value]) => {
|
||||
if (k === newValue) {
|
||||
obj[k] = value;
|
||||
}
|
||||
return obj;
|
||||
}, {});
|
||||
|
||||
return this.handleChange(Object.assign(this.mapToObject(values), newItem), event);
|
||||
}
|
||||
|
||||
this.handleChange([newValue, ...values.keys()], event);
|
||||
};
|
||||
|
||||
unselect (key, event) {
|
||||
if (!this.props.disabled) {
|
||||
const values = this.values(this.props.value);
|
||||
|
||||
values.delete(key);
|
||||
|
||||
if (this.state.isValueAnObject) {
|
||||
return this.handleChange(this.mapToObject(values), event);
|
||||
}
|
||||
|
||||
this.handleChange([...values.keys()], event);
|
||||
}
|
||||
}
|
||||
|
||||
setIsValueAnObject () {
|
||||
this.setState({
|
||||
isValueAnObject: !Array.isArray(this.props.value) && typeof this.props.value === 'object'
|
||||
});
|
||||
}
|
||||
|
||||
mapToObject (map) {
|
||||
return Array.from(map).reduce((obj, [k, value]) => {
|
||||
obj[k] = value;
|
||||
return obj;
|
||||
}, {});
|
||||
}
|
||||
|
||||
renderSelected () {
|
||||
if (this.props.multiple) {
|
||||
const selectedItems = [...this.values()].map(([key, value]) => {
|
||||
|
@ -306,7 +358,7 @@ const factory = (Chip, Input) => {
|
|||
render () {
|
||||
const {
|
||||
allowCreate, error, label, source, suggestionMatch, //eslint-disable-line no-unused-vars
|
||||
selectedPosition, keepFocusOnChange, showSuggestionsWhenValueIsSet, //eslint-disable-line no-unused-vars
|
||||
selectedPosition, keepFocusOnChange, showSuggestionsWhenValueIsSet, showSelectedWhenNotInSource, onQueryChange, //eslint-disable-line no-unused-vars
|
||||
theme, ...other
|
||||
} = this.props;
|
||||
const className = classnames(theme.autocomplete, {
|
||||
|
|
|
@ -72,11 +72,16 @@ interface AutocompleteProps extends ReactToolbox.Props {
|
|||
* @default auto
|
||||
*/
|
||||
onChange?: Function;
|
||||
/**
|
||||
* Callback function that is fired when the components's query value changes.
|
||||
* @default auto
|
||||
*/
|
||||
onQueryChange?: Function;
|
||||
/**
|
||||
* Determines if the selected list is shown above or below input. It can be above or below.
|
||||
* @default above
|
||||
*/
|
||||
selectedPosition?: "above" | "below";
|
||||
selectedPosition?: "above" | "below" | "none";
|
||||
/**
|
||||
* If true, the list of suggestions will not be filtered when a value is selected.
|
||||
* @default false
|
||||
|
|
|
@ -0,0 +1,106 @@
|
|||
import * as React from "react";
|
||||
import ReactToolbox from "../index";
|
||||
|
||||
export interface AutocompleteTheme {
|
||||
/**
|
||||
* Used for a suggestion when it's active.
|
||||
*/
|
||||
active?: string;
|
||||
/**
|
||||
* Used for the root element.
|
||||
*/
|
||||
autocomplete?: string;
|
||||
/**
|
||||
* Used when the input is focused.
|
||||
*/
|
||||
focus?: string;
|
||||
/**
|
||||
* Used to style the Input component.
|
||||
*/
|
||||
input?: string;
|
||||
/**
|
||||
* Used for the label.
|
||||
*/
|
||||
label?: string;
|
||||
/**
|
||||
* Used to style each suggestion.
|
||||
*/
|
||||
suggestion?: string;
|
||||
/**
|
||||
* Used to style the suggestions container.
|
||||
*/
|
||||
suggestions?: string;
|
||||
/**
|
||||
* Used for the suggestions when it's opening to the top.
|
||||
*/
|
||||
up?: string;
|
||||
/**
|
||||
* Classname used for a single value.
|
||||
*/
|
||||
value?: string;
|
||||
/**
|
||||
* Classname used for the values container.
|
||||
*/
|
||||
values?: string;
|
||||
}
|
||||
|
||||
interface AutocompleteProps extends ReactToolbox.Props {
|
||||
/**
|
||||
* Determines the opening direction. It can be auto, up or down.
|
||||
* @default auto
|
||||
*/
|
||||
direction?: "auto" | "up" | "down";
|
||||
/**
|
||||
* If true, component will be disabled.
|
||||
*/
|
||||
disabled?: boolean;
|
||||
/**
|
||||
* Sets the error string for the internal input element.
|
||||
* @default false
|
||||
*/
|
||||
error?: string;
|
||||
/**
|
||||
* The text string to use for the floating label element.
|
||||
*/
|
||||
label?: string;
|
||||
/**
|
||||
* If true, component can hold multiple values.
|
||||
*/
|
||||
multiple?: boolean;
|
||||
/**
|
||||
* Callback function that is fired when the components's value changes.
|
||||
* @default auto
|
||||
*/
|
||||
onChange?: Function;
|
||||
/**
|
||||
* Determines if the selected list is shown above or below input. It can be above or below.
|
||||
* @default above
|
||||
*/
|
||||
selectedPosition?: "above" | "below";
|
||||
/**
|
||||
* If true, the list of suggestions will not be filtered when a value is selected.
|
||||
* @default false
|
||||
*/
|
||||
showSuggestionsWhenValueIsSet?: boolean;
|
||||
/**
|
||||
* Object of key/values or array representing all items suggested.
|
||||
*/
|
||||
source?: any;
|
||||
/**
|
||||
* Determines how suggestions are supplied.
|
||||
* @default start
|
||||
*/
|
||||
suggestionMatch?: "start" | "anywhere" | "word";
|
||||
/**
|
||||
* Classnames object defining the component style.
|
||||
*/
|
||||
theme?: AutocompleteTheme;
|
||||
/**
|
||||
* Value or array of values currently selected component.
|
||||
*/
|
||||
value?: any;
|
||||
}
|
||||
|
||||
export class Autocomplete extends React.Component<AutocompleteProps, {}> { }
|
||||
|
||||
export default Autocomplete;
|
|
@ -5,15 +5,18 @@ class AutocompleteTest extends React.Component {
|
|||
state = {
|
||||
simple: 'Spain',
|
||||
simpleShowAll: 'England',
|
||||
multiple: ['ES-es', 'TH-th'],
|
||||
multipleArray: ['ES-es', 'TH-th'],
|
||||
multipleObject: {'ES-es': 'Spain', 'TH-th': 'Thailand'},
|
||||
countriesArray: ['Spain', 'England', 'USA', 'Thailand', 'Tongo', 'Slovenia'],
|
||||
countriesObject: {'ES-es': 'Spain', 'TH-th': 'Thailand', 'EN-gb': 'England',
|
||||
'EN-en': 'United States of America', 'EN-nz': 'New Zealand'}
|
||||
countriesObject: {
|
||||
'EN-gb': 'England',
|
||||
'EN-en': 'United States of America', 'EN-nz': 'New Zealand'
|
||||
}
|
||||
};
|
||||
|
||||
handleMultipleChange = (value) => {
|
||||
handleMultipleArrayChange = (value) => {
|
||||
this.setState({
|
||||
multiple: value,
|
||||
multipleArray: value,
|
||||
countriesObject: {
|
||||
...this.state.countriesObject,
|
||||
...(value[0] && !this.state.countriesObject[value[0]]) ? {[value[0]]: value[0]} : {}
|
||||
|
@ -21,6 +24,12 @@ class AutocompleteTest extends React.Component {
|
|||
});
|
||||
};
|
||||
|
||||
handleMultipleObjectChange = (value) => {
|
||||
this.setState({
|
||||
multipleObject: value
|
||||
});
|
||||
};
|
||||
|
||||
handleSimpleChange = (value) => {
|
||||
this.setState({simple: value});
|
||||
};
|
||||
|
@ -39,10 +48,20 @@ class AutocompleteTest extends React.Component {
|
|||
allowCreate
|
||||
keepFocusOnChange
|
||||
label="Pick multiple elements..."
|
||||
onChange={this.handleMultipleChange}
|
||||
onChange={this.handleMultipleArrayChange}
|
||||
source={this.state.countriesObject}
|
||||
suggestionMatch="anywhere"
|
||||
value={this.state.multiple}
|
||||
value={this.state.multipleArray}
|
||||
/>
|
||||
|
||||
<Autocomplete
|
||||
allowCreate
|
||||
label="Pick multiple elements with object value..."
|
||||
onChange={this.handleMultipleObjectChange}
|
||||
showSelectedWhenNotInSource
|
||||
source={this.state.countriesObject}
|
||||
suggestionMatch="anywhere"
|
||||
value={this.state.multipleObject}
|
||||
/>
|
||||
|
||||
<Autocomplete
|
||||
|
|
Loading…
Reference in New Issue