Merge branch 'show-chips-if-not-in-source' into dev

old
Craig Cartmell 2016-11-30 15:51:15 +00:00
commit d8a73eef88
6 changed files with 202 additions and 20 deletions

View File

@ -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,

View File

@ -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, {

View File

@ -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

View File

106
lib/autocomplete/index.d.ts vendored Normal file
View File

@ -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;

View File

@ -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